python基礎(chǔ)之并發(fā)編程(一)
一、進(jìn)程(Process)
是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)
二、線程(Thread)
是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn) 程中的實(shí)際運(yùn)作單位。
三、并發(fā)編程解決方案:
1、多任務(wù)的實(shí)現(xiàn)有 3 種方式:
- 多進(jìn)程模式;
- 多線程模式;
- 多進(jìn)程+多線程模式
四、多線程實(shí)現(xiàn) (兩種)
1、第一種 函數(shù)方法
# 方法包裝-啟動(dòng)多線程 from threading import Thread from time import sleep, time def func1(name): print("Threading:{} start".format(name)) sleep(3) print("Threading:{} end".format(name)) if __name__ == '__main__': # 開(kāi)始時(shí)間 start = time() # 創(chuàng)建線程列表 t_list = [] # 循環(huán)創(chuàng)建線程 for i in range(10): t = Thread(target=func1, args=('t{}'.format(i),)) t.start() t_list.append(t) # 等待線程結(jié)束 for t in t_list: t.join() # 計(jì)算使用時(shí)間 end = time() - start print(end)
2、第二種 類方法包裝
# 類包裝-啟動(dòng)多線程 from threading import Thread from time import sleep, time class MyThread(Thread): def __init__(self,name): Thread.__init__(self) self.name =name def run(self): print("Threading:{} start".format(self.name)) sleep(3) print("Threading:{} end".format(self.name)) if __name__ == '__main__': # 開(kāi)始時(shí)間 start = time() # 創(chuàng)建線程列表 t_list = [] # 循環(huán)創(chuàng)建線程 for i in range(10): t = MyThread('t{}'.format(i)) t.start() t_list.append(t) # 等待線程結(jié)束 for t in t_list: t.join() # 計(jì)算使用時(shí)間 end = time() - start print(end)
注意:
主線程不會(huì)等待子線程運(yùn)行結(jié)束,如果需要等待可使用 join()方法不要啟動(dòng)線程后立即 join(),很容易造成串行運(yùn)行,導(dǎo)致并發(fā)失效
五、守護(hù)線程與子線程
1、線程在分法有:
主線程:程序的本身
子線程:在程序另開(kāi)起的線程
2、守護(hù)線程
主要的特征是它的生命周期。主線程死亡,它也就隨之 死亡
# 類包裝-啟動(dòng)多線程 from threading import Thread from time import sleep, time class MyThread(Thread): def __init__(self,name): Thread.__init__(self) self.name =name def run(self): print("Threading:{} start".format(self.name)) sleep(3) print("Threading:{} end".format(self.name)) if __name__ == '__main__': # 開(kāi)始時(shí)間 start = time() # 循環(huán)創(chuàng)建線程 for i in range(10): t = MyThread('t{}'.format(i)) t.setDaemon(True) t.start() # 計(jì)算使用時(shí)間 end = time() - start print(end)
六、鎖
from threading import Thread def func1(name): print('Threading:{} start'.format(name)) global num for i in range(50000000): # 有問(wèn)題 #for i in range(5000): # 無(wú)問(wèn)題 num += 1 print('Threading:{} end num={}'.format(name, num)) if __name__ == '__main__': num =0 # 創(chuàng)建線程列表 t_list = [] # 循環(huán)創(chuàng)建線程 for i in range(5): t = Thread(target=func1, args=('t{}'.format(i),)) t.start() t_list.append(t) # 等待線程結(jié)束 for t in t_list: t.join()
Python 使用線程的時(shí)候,會(huì)定時(shí)釋放 GIL 鎖,這時(shí)會(huì) sleep,所以才會(huì)出現(xiàn)上面的問(wèn)題。 面對(duì)這個(gè)問(wèn)題,如果要解決此問(wèn)題,我們可以使用 Lock 鎖解決此問(wèn)題( 加鎖的目的是:保證數(shù)據(jù)安全)
from threading import Thread,Lock def func1(name): # 獲取鎖 lock.acquire() with lock: global count for i in range(100000): count += 1 # 釋放鎖 lock.release() if __name__ == "__main__": count = 0 t_list = [] # 創(chuàng)建鎖對(duì)象 lock = Lock() for i in range(10): t = Thread(target=func1,args=(f't{i+1}',)) t.start() t_list.append(t) for t in t_list: t.join() print(count)
七、死鎖
from threading import Thread, Lock #Lock 鎖 同步鎖 互斥鎖 from time import sleep def fun1(): lock1.acquire() print('fun1 拿到鍵盤') sleep(2) lock2.acquire() print('fun1 拿到鼠標(biāo)') lock2.release() print('fun1 釋放鼠標(biāo)') lock1.release() print('fun1 釋放鍵盤') def fun2(): lock2.acquire() print('fun2 拿到鼠標(biāo)') lock1.acquire() print('fun2 拿到鍵盤') lock1.release() print('fun2 釋放鍵盤') lock2.release() print('fun2 釋放鼠標(biāo)') if __name__ == '__main__': lock1 = Lock() lock2 = Lock() t1 = Thread(target=fun1) t2 = Thread(target=fun2) t1.start() t2.start()
from threading import RLock ''' Lock 鎖 同步鎖 互斥鎖 RLock 遞歸鎖 ''' def func1(): lock.acquire() print('func1獲取鎖') func2() lock.release() print('func1釋放鎖') def func2(): lock.acquire() print('func2獲取鎖') lock.release() print('func2釋放鎖') def func3(): func1() func2() if __name__ == "__main__": #lock = Lock() 會(huì)產(chǎn)生錯(cuò)誤 lock = RLock() func3()
八、信號(hào)量(Semaphore)
我們都知道在加鎖的情況下,程序就變成了串行,也就是單線程,而有時(shí),我們?cè)诓挥每?慮數(shù)據(jù)安全時(shí),為了避免業(yè)務(wù)開(kāi)啟過(guò)多的線程時(shí)。我們就可以通過(guò)信號(hào)量(Semaphore)來(lái) 設(shè)置指定個(gè)數(shù)的線程。(比如:電梯每次只能承載三個(gè)人,那么同時(shí)只能有三個(gè)人乘坐,其他人只能等別人做完才能乘坐)
from time import sleep from threading import Thread from threading import BoundedSemaphore def index(num): lock.acquire() print(f'第{num}個(gè)人乘坐??!') sleep(2) lock.release() if __name__ == "__main__": lock = BoundedSemaphore(3) for i in range(10): t = Thread(target=index,args=(f'{i+1}',)) t.start()
九、事件(Event)
Event()可以創(chuàng)建一個(gè)事件管理標(biāo)志,該標(biāo)志(event)默認(rèn)為 False,event 對(duì)象主要有 四種方法可以調(diào)用:
1、 event.wait(timeout=None):
調(diào)用該方法的線程會(huì)被阻塞,如果設(shè)置了 timeout 參數(shù),超時(shí)后,線程會(huì)停止阻塞繼續(xù)執(zhí)行;
2、event.set():
將 event 的標(biāo)志設(shè)置為 True,調(diào)用 wait 方法的所有線程將被喚 醒;
3、event.clear():
將 event 的標(biāo)志設(shè)置為 False,調(diào)用 wait 方法的所有線程將被 阻塞;
4、event.is_set():
判斷 event 的標(biāo)志是否為 True。
十、線程通信-隊(duì)列
線程安全是多線程編程時(shí)的計(jì)算機(jī)程序代碼中的一個(gè)概念。在擁有共享數(shù)據(jù)的多條線程并 行執(zhí)行的程序中,線程安全的代碼會(huì)通過(guò)同步機(jī)制保證各個(gè)線程都可以正常且正確的執(zhí)行,不 會(huì)出現(xiàn)數(shù)據(jù)污染等意外情況
1使用的隊(duì)列的好處:
1. 安全
2. 解耦
3. 提高效率
2Queue模塊中的常用方法:
Python的Queue模塊中提供了同步的、線程安全的隊(duì)列類,包括FIFO(先入先出)隊(duì)列Queue,LIFO(后入先出)隊(duì)列LifoQueue,和優(yōu)先級(jí)隊(duì)列PriorityQueue。這些隊(duì)列都實(shí)現(xiàn)了鎖原語(yǔ),能夠在多線程中直接使用??梢允褂藐?duì)列來(lái)實(shí)現(xiàn)線程間的同步
Queue.qsize()
返回隊(duì)列的大小Queue.empty()
如果隊(duì)列為空,返回True,反之FalseQueue.full()
如果隊(duì)列滿了,返回True,反之FalseQueue.full
與maxsize
大小對(duì)應(yīng)Queue.get([block[, timeout]])
獲取隊(duì)列,timeout等待時(shí)間Queue.get_nowait()
相當(dāng)Queue.get(False)
Queue.put(item)
寫入隊(duì)列,timeout等待時(shí)間Queue.put_nowait(item
) 相當(dāng)Queue.put(item, False)Queue.task_done()
在完成一項(xiàng)工作之后,Queue.task_done()函數(shù)向任務(wù)已經(jīng)完成的隊(duì)列發(fā)送一個(gè)信號(hào)Queue.join()
實(shí)際上意味著等到隊(duì)列為空,再執(zhí)行別的操作
十一、生產(chǎn)者和消費(fèi)者模式
生產(chǎn)者消費(fèi)者模式是通過(guò)一個(gè)容器來(lái)解決生產(chǎn)者和消費(fèi)者的強(qiáng)耦合問(wèn)題。生產(chǎn)者和消費(fèi)者 彼此之間不直接通訊,而通過(guò)阻塞隊(duì)列來(lái)進(jìn)行通訊,所以生產(chǎn)者生產(chǎn)完數(shù)據(jù)之后不用等待消費(fèi) 者處理,直接扔給阻塞隊(duì)列,消費(fèi)者不找生產(chǎn)者要數(shù)據(jù),而是直接從阻塞隊(duì)列里取,阻塞隊(duì)列 就相當(dāng)于一個(gè)緩沖區(qū),平衡了生產(chǎn)者和消費(fèi)者的處理能力。
from threading import Thread from queue import Queue from time import sleep def producer(): num = 1 while True: print(f'生產(chǎn)了{(lán)num}號(hào)皮卡丘') qe.put(f'{num}號(hào)皮卡丘') num += 1 sleep(1) def consumer(): print('購(gòu)買了{(lán)}'.format(qe.get())) sleep(2) if __name__ == "__main__": # 共享數(shù)據(jù)的容器 qe= Queue(maxsize=5) # 創(chuàng)建生產(chǎn)者線程 t1 = Thread(target = producer) # 創(chuàng)建消費(fèi)者線程 t2 = Thread(target = consumer) # 創(chuàng)建消費(fèi)者線程 t3 = Thread(target = consumer) # 開(kāi)始工作 t1.start() t2.start() t3.start()
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注本站的更多內(nèi)容!
版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。