python -- 多线程编程

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
  • 程序的运行速度可能加快
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。

指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

  • 线程可以被抢占(中断)。
  • 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。

python中的多线程

  多线程之间是共享内存的,而且是可以直接通信的。

1、多线程实现

 1 '''1、直接实现(最常用)'''
 2 import threading
 3 import time
 4 
 5 def hello(num):
 6     print("This threading is no [%s]" %num)
 7     time.sleep(2)
 8     
 9 if __name__ == '__main__':
10     '''
11     t1 = threading.Thread(target=hello,args=[1,])  #实例化一个线程,后面参数为列表,只有一个元素时,要有','
12     t2 = threading.Thread(target=hello,args=[2,])
13     
14     t1.start()  #上面只是实例化,这里才是启动线程
15     t2.start()
16     t1.join()
17     t2.join()
18     print('------main------')
19     
20     以上在没有t1.join()、t2.join()时,后面那句直接打印。因为启动线程后,主线程和两个子线程
21 相互独立, print('------main------')是属于主线程的,不会等待子线程结束才打印
22 
23 t1.join() 的作用是:阻塞,等待线程t1结束后猜结束父线程
24     '''
25     
26     #若有多个线程,对上面代码改进:
27     t_list = []
28     for i in range(10):
29         t = threading.Thread(target=hello,args=[i,])
30         t.start()
31         t_list.append(t)
32     
33     for i in t_list:
34         i.join()
35     
36     print('------main------')
37     
38     
39 '''2、自定义类实现多线程'''
40 import threading
41 import time
42 
43 class MyThread(threading.Thread):
44     def __init__(self,num):
45         super(MyThread,self).__init__()     #继承父类的构造函数
46         self.num = num
47         
48     #在自定义线程类里面,必须有这个run方法
49     #这个run默认是每个线程调用start函数后执行的函数
50     def run(self):
51         print("This threading is no [%s]" %self.num)
52         time.sleep(2)        
53         
54 
55 t1 = MyThread(1)
56 t2 = MyThread(2)
57 t1.start()
58 t2.start()
59 t1.join()
60 t2.join()
61 print("父线程执行结束~")
62 
63 
64 '''守护线程'''
65 '''
66 setDaemon  设置守护线程 eg:t.setDaemon(True)
67 '''
68 import time
69 import threading
70 
71 def run(n):
72     print("[%s] ------running------
" %n)
73     time.sleep(2)
74     print('---done---')
75     
76 def main():
77     for i in range(5):
78         t = threading.Thread(target=run,args=[i,])
79         t.start()
80         print("starting thread",t.getName())
81     t.join(1)
82     
83 m = threading.Thread(target=main,args=[])
84 #将m线程设置为Daemon线程,它做为程序主线程的守护线程,当主线程退出时,m线程也会退出,由m启动的其它子线程会同时退出,不管是否执行完任务
85 m.setDaemon(True)  
86 m.start()
87 m.join(2)
88 print("----main thread done---")

  注意:python的多线程是通过c语言的接口调用的系统原生的线程,所以在线程调用后,python就是去了对线程的控制能力,当线程结束后会把执行结果返回给python。

2、GIL与线程锁    

    GIL锁:全局解释器锁,它是解释器层次的,作用是控制每次只能有一个python线程在操作系统中运行
    线程锁(互斥锁):是在解释器之上控制同一时间只能一个线程修改指定变量的锁

    在多线程时,若多个线程都是修改同一份数据,那么就要对修改数据的代码部分加上线程锁
    这样能使在同一时刻,只有一个线程在修改该数据,这样才能达到预想的效果

 1 import threading
 2 import time
 3 
 4 def addNum():
 5     global num
 6     print("--get num:",num)
 7     time.sleep(1)
 8     lock.acquire()    #加锁
 9     num -= 1   #修改变量
10     lock.release()    #解锁   加锁后必须要解锁,不然程序就死了
11     
12 num = 100    
13 thread_list = []
14 lock = threading.Lock()    #实例化一个线程锁
15 
16 for i in range(100):
17     t = threading.Thread(target=addNum)
18     t.start()
19     thread_list.append(i)
20     
21 for i in thread_list:      #等待所有线程都执行完毕
22     #t.join(timeout=3)   #注意:这里设置了timeout后就不再是阻塞状态,而且,并不是等待3s...
23     t.join()    #作用:只要子线程t还没执行结束,这里就阻塞,父线程就不能退出(父线程就是执行脚本这个)
24 
25 print('finally num:',num)
26 '''
27 没加锁时,各线程执行后可能会覆盖其他线程的结果,导致结果是随机的
28 加锁后,每次都只有一个线程在操作数据,所以结果就是确定的
29 '''
30 print('All of thread was exec over~')

3、递归锁

 1 #!/usr/bin/env python
 2 #coding:utf-8
 3 
 4 '''
 5 递归锁:就是大锁里面还要包含子锁
 6 '''
 7 import threading,time
 8 
 9 def run1():
10     print("grab the first data")
11     rlock.acquire()   #捕获(加锁)
12     global num1
13     num1 += 1
14     rlock.release()   #释放(去锁)
15     return num1
16     
17 def run2():
18     print("geab the second data")
19     rlock.acquire()
20     global num2
21     num2 += 1
22     rlock.release()
23     return num2
24     
25 def run3():
26     rlock.acquire()
27     res1 = run1()
28     print("------btween run1 and run2------")
29     res2 = run2()
30     rlock.release()
31     print("res1:%s , res2: %s" %(res1,res2))
32     
33 if __name__ == "__main__":
34     num1,num2 = 0,0
35     rlock = threading.RLock()    #实例化一个递归锁
36     for i in range(10):
37         t = threading.Thread(target=run3)
38         t.start()
39         
40     while threading.active_count() != 1:
41         print("剩余线程个数:",threading.active_count())
42     else:
43         print("剩余线程个数:",threading.active_count())
44         print('------all threads done------')
45         print("最后 num1:%s  num2: %s" %(num1,num2))

4、信号量

 1 #!/usr/bin/env python
 2 #coding:utf-8
 3 '''
 4 信号量:互斥锁同时只允许一个线程更改数据,而Semaphore(信号量)是同时允许一定数量的线程更改数据
 5 '''
 6 import threading,time
 7 
 8 def run(n):
 9     semaphore.acquire()
10     time.sleep(1)
11     print("run the thread: %s
" %n)
12     semaphore.release()
13     
14 if __name__ == '__main__':
15     
16     semaphore = threading.BoundedSemaphore(3)  #最多允许3个线程同时运行
17     for i in range(20):
18         t = threading.Thread(target=run,args=[i,])
19         t.start()
20         
21 while threading.active_count() != 1:
22     #print('当前剩余线程数:',threading.active_count())
23     pass
24 else:
25     print("------ all threads done ------")
26     

5、接收线程返回的数据

 1 #!/usr/bin/env python
 2 # coding:utf-8
 3 import time
 4 import threading
 5 
 6 data = []
 7 def func(n):
 8     time.sleep(3)
 9     data.append(n**n)
10     print('hello',n)
11     return n**n
12 
13 thread_list = []
14 for i in range(20):
15     #t = threading.Thread(target=func,args=[i,]) #这里传参时,参数以列表形式后面可以加',',也可以不加;以元组形式必须加
16     t = threading.Thread(target=func,args=(i,)) 
17     t.start()
18     thread_list.append(t)
19     
20 #for i in thread_list:
21     #i.join()
22     #print('hello ',i)
23 
24 #print(thread_list)
25 print(data)
26 print('----main thread end----')

6、event模块

 1 #!/usr/bin/env python
 2 # coding:utf-8
 3 
 4 '''
 5 通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
 6 个人理解:
 7     通过Event实例化一个对象,这个对象用set方法设置一直flag,所有线程都能访问flag,并根据flag不同的值做不同的处理
 8 '''
 9 import threading
10 import time
11 
12 
13 def light():
14     if not event.isSet():
15         event.set()   # set后,wait就不阻塞
16     count = 0
17     while True:
18         if count < 10:
19             print(' 33[42;1m--green light on---33[0m')
20         elif count < 13:
21             print(' 33[43;1m--yellow light on---33[0m ')
22         elif count < 20:
23             if event.isSet():
24                 event.clear()
25             print(' 33[41;1m--red light on---33[0m ')
26         else:
27             count = 0
28             event.set() 
29         time.sleep(1)
30         count += 1
31 
32 
33 def car(n):
34 
35     ''' 
36     :param n:  给车辆的编号 
37     :return:  默认返回值是None
38     '''
39 
40     while True:
41         time.sleep(1)
42         if event.isSet():
43             print('car [%s] is running...' % n)
44         else:
45             print('car [%s] is waiting for the red light...' % n)
46             event.wait()
47 
48 if __name__ == "__main__":
49     event = threading.Event()   # 实例化一个Event对象
50     Light = threading.Thread(target=light)
51     Light.start()
52     for i in range(3):
53         Car = threading.Thread(target=car,args=(i,))
54         Car.start() 
原文地址:https://www.cnblogs.com/xtsec/p/6964150.html