并发编程之线程
关于线程的一些事儿
理论知识
全局解释器锁GIL
python代码的执行有python虚拟机(也叫解释器主循环)来控制,python在设计之处就考虑到要在主循环中,同时只有一个线程在执行,虽然python解释器可以运行多个线程,但是任意时刻只有一个线程在解释器中运行。
对python虚拟机的访问有全局解释器锁(GIL)来控制,正式这个锁能保证同一时刻只有一个线程在运行。
python线程模块的选择
python提供了几个用于多线程编程的模块,包括thread,threading和Queue等。thread和threading模块允许程序员创建和管理线程,thread模块提供了基本的线程和锁的支持,threading提供了更高级别,功能更强的线程管理的功能,Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
threading模块
multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性。
线程的创建Threading.Thread类
from threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.start() print('主线程')
from threading import Thread import time class Sayhi(Thread): def __init__(self,name): super().__init__() self.name=name def run(self): time.sleep(2) print('%s say hello' % self.name) if __name__ == '__main__': t = Sayhi('egon') t.start() print('主线程')
锁
互斥锁
线程之间的数据安全问题:
+=-= 赋值操作不安全(实际是含有三步骤:在取值后返回值时会有另外线程取值这样取的值还是未改变的)
另外互斥锁可以开多个锁只要不一致的锁名 例:
noodle_lock = Lock()
fork_lock = Lock()
from threading import Thread import os,time def work(): global n temp=n time.sleep(0.1) n=temp-1 if __name__ == '__main__': n=100 l=[] for i in range(100): p=Thread(target=work) l.append(p) p.start() for p in l: p.join()
递归锁
死锁现象
两把锁
异步的
操作的时候 抢到 一把锁之后还要再去抢第二把锁
一个线程抢到一把锁
另外一个线程抢到另一把锁
递归锁能快速解决死锁问题
# import time # from threading import Thread,Lock # # noodle_lock = Lock() # fork_lock = Lock() # def eat1(name): # noodle_lock.acquire() # print('%s拿到面条了'%name) # fork_lock.acquire() # print('%s拿到叉子了'%name) # print('%s吃面'%name) # time.sleep(0.3) # fork_lock.release() # print('%s放下叉子'%name) # noodle_lock.release() # print('%s放下面'%name) # # def eat2(name): # fork_lock.acquire() # print('%s拿到叉子了' % name) # noodle_lock.acquire() # print('%s拿到面条了'%name) # print('%s吃面'%name) # time.sleep(0.3) # noodle_lock.release() # print('%s放下面'%name) # fork_lock.release() # print('%s放下叉子' % name) # # if __name__ == '__main__': # name_list = ['alex','wusir'] # name_list2 = ['nezha','yuan'] # for name in name_list: # Thread(target=eat1,args=(name,)).start() # for name in name_list2: # Thread(target=eat2,args=(name,)).start()
# import time # from threading import Thread,RLock # # fork_lock = noodle_lock = RLock() # def eat1(name): # noodle_lock.acquire() # print('%s拿到面条了'%name) # fork_lock.acquire() # print('%s拿到叉子了'%name) # print('%s吃面'%name) # time.sleep(0.3) # fork_lock.release() # print('%s放下叉子'%name) # noodle_lock.release() # print('%s放下面'%name) # # def eat2(name): # fork_lock.acquire() # print('%s拿到叉子了' % name) # noodle_lock.acquire() # print('%s拿到面条了'%name) # print('%s吃面'%name) # time.sleep(0.3) # noodle_lock.release() # print('%s放下面'%name) # fork_lock.release() # print('%s放下叉子' % name) # # if __name__ == '__main__': # name_list = ['alex','wusir'] # name_list2 = ['nezha','yuan'] # for name in name_list: # Thread(target=eat1,args=(name,)).start() # for name in name_list2: # Thread(target=eat2,args=(name,)).start()
递归锁好不好?
递归锁并不是一个好的解决方案
死锁现象的发生不是互斥锁的问题
而是程序员的逻辑有问题导致的
递归锁能够快速的解决死锁问题
递归锁
迅速恢复服务 递归锁替换互斥锁
在接下来的时间中慢慢把递归锁换成互斥锁
信号量
同进程的一样
Semaphore管理一个内置的计数器,每当调用acquire()时内置的计数器-1,调用release()时内置计数器+1,计数器不能小于0,当计数器为0,acquire()将阻塞线程直到其他线程调用release()
import time from threading import Semaphore,Thread def func(index,sem): sem.acquire() print(index) time.sleep(1) sem.release() if __name__ == '__main__': sem = Semaphore(5) for i in range(10): Thread(target=func,args=(i,sem)).start()
事件
和进程差不多
import time import random from threading import Event,Thread def check(): print('开始检测数据库连接') time.sleep(random.randint(1,5)) # 检测数据库连接 e.set() # 成功了 def connect(): for i in range(3): e.wait(0.5) if e.is_set(): print('数据库连接成功') break else: print('尝试连接数据库%s次失败'%(i+1)) else: raise TimeoutError e = Event() Thread(target=connect).start() Thread(target=check).start()
条件Condition
notify和wait都需要加上锁
from threading import Condition,Thread def func(con,index): print('%s在等待'%index) con.acquire() con.wait() print('%s do something'%index) con.release() con = Condition() for i in range(10): t = Thread(target=func,args=(con,i)) t.start() con.acquire() con.notify_all() con.release() # count = 10 # while count > 0: # num= int(input('>>>')) # con.acquire() # con.notify(num) # count -= num # con.release()
定时器
from threading import Timer def func(): print('执行我啦') t = Timer(3600,func) t.start() print('主线程')
队列
LifoQueue
PriorityQueue
# from queue import LifoQueue # 栈 完成算法 # lq = LifoQueue() # lq.put(1) # lq.put(2) # lq.put(3) # lq.put('a') # lq.put('b') # print(lq.get()) # print(lq.get()) # print(lq.get())
# from queue import PriorityQueue # pq = PriorityQueue() # pq.put((15,'abc')) # pq.put((5,'ghi')) # pq.put((12,'def')) # pq.put((12,'aaa')) # # print(pq.get()) # print(pq.get()) # print(pq.get()) # from queue import PriorityQueue # pq = PriorityQueue() # pq.put(15) # pq.put(5) # pq.put(12) # pq.put(12) # # print(pq.get()) # print(pq.get()) # print(pq.get()) # from queue import PriorityQueue # pq = PriorityQueue() # pq.put('abc') # pq.put('def') # # print(pq.get()) # print(pq.get())