一、GIL全局解释器锁
1.GIL
全局解释器锁 只在Cpython解释器中
GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全
用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)
GIL的存在是因为Cpython解释器的内存管理不是线程安全的
2.内存管理>>>垃圾回收机制
(1)引用计数
(2)标记清楚
(3)分代回收
3.同一进程下的多个线程在同一时刻只能有一个线程被执行
4.线程是直接嫩够被cpu执行吗?
必须先抢解释器才能被cpu执行
5.GIL是加在Cpython解释器上的一把锁,并不能保证数据的安全
如果你想保证数据的安全,就必须加不同的锁
6.多线程是不是没用?
例如:四个任务:计算密集的任务 每个任务耗时10s
单核情况下:
多线程好一点,消耗的资源少一点
多核情况下:
开四个进程:10s多一点
开四个线程:40s多一点
四个任务:IO密集的任务 每个任务io 10s
单核情况下:
多线程好一点
多核情况下:
多线程好一点
多线程和多进程都有自己的优点,要根据项目需求合理选择
计算密集型
from multiprocessing import Process from threading import Thread import os,time def work(): res=0 for i in range(100000000): res*=i if __name__ == '__main__': l=[] print(os.cpu_count()) # 本机为8核 start=time.time() for i in range(8): # p=Process(target=work) #耗时9.252728939056396 p=Thread(target=work) #耗时35.369622230529785 l.append(p) p.start() for p in l: p.join() stop=time.time() print('run time is %s' %(stop-start))
IO密集型
from multiprocessing import Process from threading import Thread import threading import os,time def work(): time.sleep(2) if __name__ == '__main__': l=[] print(os.cpu_count()) #本机为8核 start=time.time() for i in range(600): p=Process(target=work) #耗时4.699530839920044 # p=Thread(target=work) #耗时2.054128885269165 l.append(p) p.start() for p in l: p.join() stop=time.time() print('run time is %s' %(stop-start))
7.补充
TCP实现并发
客户端
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: client.send(b'hello') data = client.recv(1024) print(data.decode('utf-8'))
服务端
""" 服务端 1.要有固定的IP和PORT 2.24小时不间断提供服务 3.能够支持并发 """ server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) def talk(conn): while True: try: data = conn.recv(1024) if len(data) == 0:break print(data.decode('utf-8')) conn.send(data.upper()) except ConnectionResetError as e: print(e) break conn.close() while True: conn, addr = server.accept() # 监听 等待客户端的连接 阻塞态 print(addr) t = Thread(target=talk,args=(conn,)) t.start()
二、GIL与普通的互斥锁
对于不同的数据,要想保证安全,需要加不同的锁处理
GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程
保证的是同一个进程下多个线程之间的安全
from threading import Thread import time n = 100 def task(): global n tmp = n # time.sleep(1) n = tmp -1 t_list = [] for i in range(100): t = Thread(target=task) t.start() t_list.append(t) for t in t_list: t.join() print(n)
三、死锁与递归锁
1.死锁
每acquire一次锁身上的计数加1
每release一次锁身上的计数减1
只要锁的计数不为0,其他人都不能抢
from threading import Thread,Lock,current_thread,RLock import time mutexA = Lock() mutexB = Lock() class MyThread(Thread): def run(self): # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发 self.func1() self.func2() def func1(self): mutexA.acquire() print('%s抢到了A锁'%self.name) # self.name等价于current_thread().name mutexB.acquire() print('%s抢到了B锁'%self.name) mutexB.release() print('%s释放了B锁'%self.name) mutexA.release() print('%s释放了A锁'%self.name) def func2(self): mutexB.acquire() print('%s抢到了B锁'%self.name) time.sleep(1) mutexA.acquire() print('%s抢到了A锁' % self.name) mutexA.release() print('%s释放了A锁' % self.name) mutexB.release() print('%s释放了B锁' % self.name) for i in range(10): t = MyThread() t.start()
2.递归锁
Rlock可以被第一个抢到锁的人连续的acquire和release
from threading import Thread,Lock,current_thread,RLock import time mutexA = mutexB = RLock() # A B现在是同一把锁 class MyThread(Thread): def run(self): # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发 self.func1() self.func2() def func1(self): mutexA.acquire() print('%s抢到了A锁'%self.name) # self.name等价于current_thread().name mutexB.acquire() print('%s抢到了B锁'%self.name) mutexB.release() print('%s释放了B锁'%self.name) mutexA.release() print('%s释放了A锁'%self.name) def func2(self): mutexB.acquire() print('%s抢到了B锁'%self.name) time.sleep(1) mutexA.acquire() print('%s抢到了A锁' % self.name) mutexA.release() print('%s释放了A锁' % self.name) mutexB.release() print('%s释放了B锁' % self.name) for i in range(10): t = MyThread() t.start()
四、信号量
互斥锁:一个厕所(一个坑位)
信号量:公共厕所(多个坑位)
from threading import Semaphore,Thread import time import random sm = Semaphore(5) # 造了一个含有五个的坑位的公共厕所 def task(name): sm.acquire() print('%s占了一个坑位'%name) time.sleep(random.randint(1,3)) sm.release() for i in range(40): t = Thread(target=task,args=(i,)) t.start()
五、event事件
from threading import Event,Thread import time import random event = Event() def light(): print('红灯亮着!') time.sleep(3) event.set() # 解除阻塞,给我的event发了一个信号 print('绿灯亮了!') def car(i): print('%s 正在等红灯了'%i) event.wait() # 阻塞 print('%s 加油门飙车了'%i) t1 = Thread(target=light) t1.start() for i in range(10): t = Thread(target=car,args=(i,)) t.start()
六、线程queue
1.同一进程下的多个线程本来就是数据共享,为什么还要用队列
因为队列是管道+锁 使用队列你就不需要自己手动操作锁的问题
因为锁操作的不好极容易产生死锁现象
2.Queue 普通q
import queue q=queue.Queue(3) q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get())
3.LifoQueue 先进后出q
import queue q = queue.LifoQueue(5) q.put(1) q.put(2) q.put(3) q.put(4) print(q.get()) # 4
4.PriorityQueue 优先级q(数值小的先出)
import queue q = queue.PriorityQueue() q.put((10,'a')) q.put((-1,'b')) q.put((100,'c')) print(q.get()) print(q.get()) print(q.get())