補充點
1.死鎖
當你知道鎖的使用搶鎖必須要釋放鎖,其實你在操作鎖的時候也極其容易產生死鎖現象(整個程式卡死 阻塞)
from threading import Thread, Lock
import time
mutexA = Lock()
mutexB = Lock()
# 類只要加括號多次 產生的肯定是不同的物件
# 如果你想要實作多次加括號等到的是相同的物件 單例模式
class MyThead(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print('%s 搶到A鎖'% self.name) # 獲取當前執行緒名
mutexB.acquire()
print('%s 搶到B鎖'% self.name)
mutexB.release()
mutexA.release()
def func2(self):
mutexB.acquire()
print('%s 搶到B鎖'% self.name)
time.sleep(2)
mutexA.acquire()
print('%s 搶到A鎖'% self.name) # 獲取當前執行緒名
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = MyThead()
t.start()
2.遞回鎖
遞回鎖的特點:
可以被連續的acquire和release
但是只能被第一個搶到這把鎖執行上述操作
它的內部有一個計數器 每acquire一次計數加一 每realse一次計數減一
只要計數不為0 那么其他人都無法搶到該鎖
# 代碼只需要將上述死鎖的
mutexA = Lock()
mutexB = Lock()
# 換成
mutexA = mutexB = RLock()
# 這樣死鎖的程式就可以正常運行下去
3.信號量
信號量在不同階段可能對應不同的技術點,在并發編程中,信號量指的是鎖!!
信號量與互斥鎖的比較:信號量可以看成是多個坑位,互斥鎖只能是一個坑位!!
信號量的具體語法:
from threading import Thread, Semaphore # 信號量的模塊
import time,random
sm = Semaphore(5) # 表示同時開設5個坑位(互斥鎖只開設一個)
def task(name):
sm.acquire() # 加鎖
print('%s正在蹲坑'%name)
time.sleep(random.randint(1,3)) # 蹲坑時間不等
sm.release() # 釋放鎖
if __name__ == '__main__':
for i in range(20): # 20個人去搶5個廁所
t = Thread(target=task,args=(i,))
t.start()
4.Event事件
主行程/執行緒等待子行程/執行緒運行結束時,我們使用的是join方法!
而需要子行程/執行緒之間的相互的等待結束,需要用到event事件
from threading import Thread,Event
import time
# 1.先產生一個event物件
event = Event()
def person():
print('你來了')
time.sleep(2)
event.set() # 2.發送信號,表示我執行完畢了,你可以結束了
def my():
event.wait() # 3.接收到信號,好的我開始執行
print('我走了')
if __name__ == '__main__':
t = Thread(target=person)
t1 = Thread(target=my)
t1.start()
t.start()
5.行程池和執行緒池(重要掌握)
5.1 什么是池?
池是用來保證計算機硬體安全的情況下最大限度的利用計算機,它降低了程式運行的效率但是保證了計算機硬體的安全,從而讓你的程式正常的運行!!
5.2 執行緒池的使用
# 1.匯入行程池/執行緒池模塊
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time
# 2.創建一個執行緒池物件,該物件括號內不傳引數,默認是開設當前cpu個數*5的執行緒
pool = ThreadPoolExecutor(5) # 傳入引數5,表示池子里固定創建了5個執行緒,這5個執行緒不會重復創建和銷毀
def task(name):
print('%s來了'% name)
time.sleep(2)
# 3.朝池子中提交任務,第一個引數是函式名,第二個引數是函式需要傳入的引數
"""
同步提交:提交任務之后原地等待回傳結果,不往下執行任務
異步提交:提交任務之后不原地等待回傳結果,繼續往下執行
"""
# pool.submit(task,'zhang') # 注意:這里的提交是一個異步提交
# print('主')
# 優化
list = []
for i in range(20): # 向池子中提交20個任務,池子中只有5個執行緒
res = pool.submit(task,i) # 池方法異步提交之后有一個回傳值,該回傳值是一個future物件
# print(res.result()) # 該物件有一個result方法,可以用來回傳對應執行緒函式的回傳值,沒有回傳值則回傳none
# result方法的使用使得submi異步提交變成了同步提交
list.append(res)
# 如果想等待所有的執行緒全部執行完畢,在往下執行代碼,可以使用池的shutdown方法
pool.shutdown() # shutdown的作用是等待所有執行緒池運行完畢,再關閉所有執行緒池往下運行
for i in list:
print(i.result())
5.3 行程池的使用
基本同執行緒池,不同的是以下部分:
# 1.匯入行程池/執行緒池模塊
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time,os
pool = ProcessPoolExecutor(5) # 不傳默認是cpu的個數
def task(name):
print('%s來了'% name)
time.sleep(2)
# call_back函式里面需要傳一個引數,該引數就是res(一個future物件)
def call_back(n):
# 可以通過n.result()的方法獲取task函式的回傳值
print(n.result())
if __name__ == '__main__':
for i in range(20):
# add_done_callback方法是給submit這個異步提交添加一個回呼機制,完成任務之后立刻回呼結果,方法的引數是一個函式
res = pool.submit(task, i).add_done_callback(call_back)
協程
1.什么是協程
協程其實就是一種在單執行緒下實作并發的操作,程式猿通過檢測自己寫的代碼,一旦遇到了IO操作的代碼,我們就撰寫代碼進行切換,這樣使得cpu一直處于運行狀態,提高程式的運行效率!
2.協程的具體寫法
gevent模塊的參考時為了使得函式之間不斷的切換+保存狀態!!
"""
使用協程,就必須要我們通過代碼實時檢測程式是否進入到了IO操作,如果進入了IO操作,
就進行快速的切換,以此來實作并發的效果,提升程式的運行效率
"""
# 1.匯入gevent模塊的spawn模塊,該模塊是用來監測函式里面的IO操作的,但是spawn模塊是無法監測一些常見的IO操作的
# 因此,需要匯入gevent模塊中的monkey模塊,以此來監測所有的IO操作
from gevent import monkey,spawn
monkey.patch_all() # 猴子補丁
import time
def func1():
print('hhh')
time.sleep(2)
print('lalala')
def func2():
print('wwww')
time.sleep(3)
print('結束')
start_time = time.time()
g1 = spawn(func1) # spawn括號里是需要啟動監測的函式名,函式名后面可以加需要傳入的引數
g2 = spawn(func2) # 該方法是異步提交,有一個回傳值,如果沒有join方法會自動往下執行代碼,結束程式
g2.join() # 等待函式執行完在往下執行
g1.join()
print(time.time()-start_time)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/502965.html
標籤:Python
上一篇:JSP中的JSTL 標簽庫
下一篇:pip下載慢問題解決方案
