GIL只能保证垃圾回收机制的安全,进程中的数据安全还是需要自定义锁
线程执行代码首先要抢到GIL全局锁,假设线程X首先抢到,以为要抢到自定义锁要执行代码,所以这个线程在执行代码的时候就很容抢到了自定义锁,当线程在执行代码的的时候遇到IO操作就会被CPU检测到,并且夺回CPU的执行权限,这个线程就释放了GIL全局锁,其他线程就开始抢GIL全局锁,但是即便是抢到了GIL全局锁,但是自定义的锁还在那个线程那里,所以那个线程做完IO操作,其他线程还是要把GIL全局锁还回去,他才能执行剩下的代码释放自定义锁,于是这个线程又拿到了GIL全局锁,执行完代码后又释放了GIL锁,在释放自定义锁的时候又加入了抢GIL锁的大军中.....
这样子就保证了进程中数据的安装
1. GIL全局解释器锁(******) 2. 死锁与递归锁 3. 信号量 4. Event事件 5. 线程queue 一、GIL全局锁 运行test.py的流程: a、将python解释器的代码从硬盘读入内存 b、将test.py的代码从硬盘读入内存 (一个进程内装有两份代码) c、将test.py中的代码像字符串一样读入python解释器中解析执行 1 、GIL:全局解释器锁 (CPython解释器的特性) In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management (垃圾回收机制,由解释器定期执行)is not thread-safe(如果不是串行改数据,当x=10的过程中内存中产生一个10,还没来的及绑定x,就有可能被垃圾回收机制回收).However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) GIL本质就是一把夹在解释器身上的互斥锁(执行权限)。同一个进程内的所有线程都需要先抢到GIL锁,才能执行解释器代码 2、GIL的优缺点: 优点:保证Cpython解释器内存管理的线程安全 缺点:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,也就说Cpython解释器的多线程无法实现并行无法利用多核优势 注意: a、GIL不能并行,但有可能并发,不一定为串行。因为串行是一个任务完完全全执行完毕后才进行下一个;而cpython中,一个线程在io时,被CPU释放时,会被强行取消GIL的使用权限 b、多核(多CPU)的优势是提升运算效率 c、计算密集型--》使用多进程,以用上多核 d、IO密集型--》使用多线程 二、Cpython解释器并发效率验证 1、计算密集型应该使用多进程 from multiprocessing import Process from threading import Thread import time # import os # print(os.cpu_count()) #查看cpu个数 def task1(): res=0 for i in range(1,100000000): res+=i def task2(): res=0 for i in range(1,100000000): res+=i def task3(): res=0 for i in range(1,100000000): res+=i def task4(): res=0 for i in range(1,100000000): res+=i if __name__ == '__main__': # p1=Process(target=task1) # p2=Process(target=task2) # p3=Process(target=task3) # p4=Process(target=task4) p1=Thread(target=task1) p2=Thread(target=task2) p3=Thread(target=task3) p4=Thread(target=task4) start_time=time.time() p1.start() p2.start() p3.start() p4.start() p1.join() p2.join() p3.join() p4.join() stop_time=time.time() print(stop_time - start_time) 2、IO密集型应该使用多线程 from multiprocessing import Process from threading import Thread import time def task1(): time.sleep(3) def task2(): time.sleep(3) def task3(): time.sleep(3) def task4(): time.sleep(3) if __name__ == '__main__': # p1=Process(target=task1) # p2=Process(target=task2) # p3=Process(target=task3) # p4=Process(target=task4) # p1=Thread(target=task1) # p2=Thread(target=task2) # p3=Thread(target=task3) # p4=Thread(target=task4) # start_time=time.time() # p1.start() # p2.start() # p3.start() # p4.start() # p1.join() # p2.join() # p3.join() # p4.join() # stop_time=time.time() # print(stop_time - start_time) #3.138049364089966 p_l=[] start_time=time.time() for i in range(500): p=Thread(target=task1) p_l.append(p) p.start() for p in p_l: p.join() print(time.time() - start_time) 三、线程互斥锁与GIL对比 GIL能保护解释器级别代码(和垃圾回收机制有关)但保护不了其他共享数据(比如自己的代码)。所以在程序中对于需要保护的数据要自行加锁 from threading import Thread,Lock import time mutex=Lock() count=0 def task(): global count mutex.acquire() temp=count time.sleep(0.1) count=temp+1 mutex.release() if __name__ == '__main__': t_l=[] for i in range(2): t=Thread(target=task) t_l.append(t) t.start() for t in t_l: t.join() print('主',count) 四、基于多线程实现并发的套接字通信 服务端: from socket import * from threading import Thread from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor tpool=ThreadPoolExecutor(3) #进程和线程都不能无限多,导入模块来限制进程和线程池重点数目;进程线程池中封装了Process、Thread模块的功能 def communicate(conn,client_addr): while True: # 通讯循环 try: data = conn.recv(1024) if not data: break conn.send(data.upper()) except ConnectionResetError: break conn.close() def server(): server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',8080)) server.listen(5) while True: # 链接循环 conn,client_addr=server.accept() print(client_addr) # t=Thread(target=communicate,args=(conn,client_addr)) # t.start() tpool.submit(communicate,conn,client_addr) server.close() if __name__ == '__main__': server() 客户端: from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: msg=input('>>>: ').strip() if not msg:continue client.send(msg.encode('utf-8')) data=client.recv(1024) print(data.decode('utf-8')) client.close() 六、死锁与递归锁 进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁 from threading import Thread,Lockimport time mutexA=Lock() mutexB=Lock() class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('