线程相关(二)
一. TCP server端通过线程实现并发
server端
1 from threading import Thread 2 import socket 3 4 sk = socket.socket() 5 sk.bind(('127.0.0.1',8080)) 6 sk.listen() 7 8 9 def talk(conn): 10 while True: 11 try: 12 data = conn.recv(1024) 13 print(data.decode('utf-8')) 14 conn.send(b'hello!') 15 except ConnectionRestError as e: 16 break 17 while True: 18 conn,addr = sk.accept() 19 print(addr) 20 t = Thread(target = talk,args = (conn,)) 21 t.start()
二.GIL全局解释器锁
是CPython中的解释器锁
通过一个例子了解GIL锁的存在
1 from threading import Thread 2 import time 3 n = 100 4 5 def task(): 6 global n 7 tmp = n 8 time.sleep(3) 9 n = tmp -1 10 11 t_list = [] 12 for i in range(100): 13 t = Thread(target = task) 14 t.start() 15 t_list.append(t) 16 17 for t in t_list: 18 t.join() 19 20 print(n)
在这种情况下 执行结果是99 因为这里有time.sleep(3) 这是IO操作,会阻塞,在此处线程会释放GIL锁,等待下一个线程来抢,因此直到最后一个线程抢到锁 才做最后的减1操作 因此最终的结果是99
如果将此处的time.sleep(3)注释掉之后 执行结果则是0
研究python 的多线程是否有用需要分情况讨论
1.四个任务 计算密集型的
单核情况下
开线程比开进程更省资源
所以开线程好
多核情况下
开进程 要10S多
开线程 要40s多
2.四个任务 IO密集型 会不停的阻塞 不停的切
单核情况下
开线程更节省资源
多核情况下
开线程更节省资源
三.死锁
下面有个死锁的例子
1 from threading import Thread,Lock 2 import time 3 4 mutexA = Lock() 5 mutexB = Lock() 6 7 class MyThread(Thread): 8 def run(self): 9 self.func1() 10 self.func2() 11 12 def func1(self): 13 mutexA.acquire() 14 print('%s抢到了A锁'%self.name) #self.name 等价于 current_thread().name 15 mutexB.acquire() 16 print('%s抢到了B锁'%self.name) 17 mutexB.release() 18 print('%s释放了B锁'%self.name) 19 mutexA.release() 20 print('%s释放了A锁'%self.name) 21 22 def func2(self): 23 mutexB.acquire() 24 print('%s抢到了B锁' % self.name) 25 time.sleep(1) 26 mutexA.acquire() 27 print('%s抢到了A锁' % self.name) 28 mutexA.release() 29 print('%s释放了A锁' % self.name) 30 mutexB.release() 31 print('%s释放了B锁' % self.name) 32 33 for i in range(10): 34 t = MyThread() 35 t.start() 36 #结果: 37 ''' 38 Thread-1抢到了A锁 39 Thread-1抢到了B锁 40 Thread-1释放了B锁 41 Thread-1释放了A锁 42 Thread-1抢到了B锁 43 Thread-2抢到了A锁 44 死锁现象出现了 45 自己千万不要轻易处理锁的问题 46 '''
根据结果再推导一遍,首先线程一 抢到了A锁和B锁,然后释放B锁,现在的B锁没有人抢,因为A锁还在线程一身上没人能抢B锁,然后线程一将A锁释放,接着线程一执行func2方法抢到了B锁,而A锁在刚才释放的一瞬间被线程二抢走了,所以现在 线程一有B锁,线程二有A锁,之后线程一在func2方法中需要抢A锁,而线程二需要B锁,但是这两把锁再对方身上,所以这样就成了死锁现象。
四.递归锁
from threading import Thread,RLock #递归锁 可以连续acquire import time ''' RLock 可以被第一个抢到锁的人连续的acquire 和 release 每acquire一次 锁身上的计数加1 每release一次 锁身上的计数减1 只要锁的计数不为0 其他人不能抢 ''' mutexA = mutexB = RLock() #这是一把锁 同一把锁 class MyThread(Thread): def run(self): 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() #结果: ''' Thread-1抢到了A锁 Thread-1抢到了B锁 Thread-1释放了B锁 Thread-1释放了A锁 Thread-1抢到了B锁 Thread-1抢到了A锁 Thread-1释放了A锁 Thread-1释放了B锁 Thread-2抢到了A锁 Thread-2抢到了B锁 Thread-2释放了B锁 Thread-2释放了A锁 Thread-2抢到了B锁 Thread-2抢到了A锁 Thread-2释放了A锁 Thread-2释放了B锁 Thread-4抢到了A锁 Thread-4抢到了B锁 Thread-4释放了B锁 Thread-4释放了A锁 Thread-4抢到了B锁 Thread-4抢到了A锁 Thread-4释放了A锁 Thread-4释放了B锁 Thread-6抢到了A锁 Thread-6抢到了B锁 Thread-6释放了B锁 Thread-6释放了A锁 Thread-6抢到了B锁 Thread-6抢到了A锁 Thread-6释放了A锁 Thread-6释放了B锁 Thread-8抢到了A锁 Thread-8抢到了B锁 Thread-8释放了B锁 Thread-8释放了A锁 Thread-8抢到了B锁 Thread-8抢到了A锁 Thread-8释放了A锁 Thread-8释放了B锁 Thread-10抢到了A锁 Thread-10抢到了B锁 Thread-10释放了B锁 Thread-10释放了A锁 Thread-10抢到了B锁 Thread-10抢到了A锁 Thread-10释放了A锁 Thread-10释放了B锁 Thread-5抢到了A锁 Thread-5抢到了B锁 Thread-5释放了B锁 Thread-5释放了A锁 Thread-5抢到了B锁 Thread-5抢到了A锁 Thread-5释放了A锁 Thread-5释放了B锁 Thread-9抢到了A锁 Thread-9抢到了B锁 Thread-9释放了B锁 Thread-9释放了A锁 Thread-9抢到了B锁 Thread-9抢到了A锁 Thread-9释放了A锁 Thread-9释放了B锁 Thread-7抢到了A锁 Thread-7抢到了B锁 Thread-7释放了B锁 Thread-7释放了A锁 Thread-7抢到了B锁 Thread-7抢到了A锁 Thread-7释放了A锁 Thread-7释放了B锁 Thread-3抢到了A锁 Thread-3抢到了B锁 Thread-3释放了B锁 Thread-3释放了A锁 Thread-3抢到了B锁 Thread-3抢到了A锁 Thread-3释放了A锁 Thread-3释放了B锁 '''
五.信号量
信号量可能再不同的领域中,对应不同的知识点
这里将信号量与互斥锁做个对比
互斥锁 :一个坑位的厕所
信号量:多个坑位的厕所
例子
1 rom threading import Semaphore,Thread 2 import time 3 sm = Semaphore(5) #造了一个含有5个坑位的公共厕所 4 5 def task(name): 6 sm.acquire() 7 print('%s占了一个坑位'%name) 8 time.sleep(1) 9 sm.release() 10 11 12 for i in range(40): 13 t = Thread(target=task,args=(i,)) 14 t.start()
六.event事件
from threading import Event,Thread import time #先生成一个event对象 e = Event() def light(): print('红灯亮..') time.sleep(3) e.set() #发信号 print('绿灯亮..') def car(name): print('%s正在等红灯..'%name) e.wait() #等待信号 print('%s加油门飙车'%name) t = Thread(target=light) t.start() for i in range(3): t = Thread(target=car,args=('伞兵%s'%i,)) t.start()4 #执行结果 红灯亮.. 伞兵0正在等红灯.. 伞兵1正在等红灯.. 伞兵2正在等红灯.. 绿灯亮.. 伞兵0加油门飙车 伞兵1加油门飙车 伞兵2加油门飙车
七.线程q
同一个进程下的多个线程本来就是数据共享的
为什么还用队列呢?
因为队列是 管道+锁
使用队列就不需要自己手动操作锁的问题
因为锁操作的不好极容易产生死锁现象
三个常见的:
1.
1 import queue 2 q = queue.Queue() 3 q.put('haha') 4 print(q.get())
2.
import queue q = queue.LifoQueue() #后进先出 q.put(1) q.put(2) q.put(3) print(q.get())
3.
import queue q = queue.PriorityQueue() #数字越小 优先级越高 q.put((10,'haha')) q.put((-10,'h3h3')) q.put((0,'xxx')) print(q.get()) print(q.get())
万般皆下品,唯有读书高!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)