多线程类似于同时执行多个不同程序,多线程运行有如下优点:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理。
- 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
- 程序的运行速度可能加快
- 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
每个线程都有他自己的一组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()