進程間通信
進程間通訊有多種方式,包括信號,管道,消息隊列等
進程間通信我們可以用Queue
Queue有兩個方法:
Put方法:以插入數據到隊列中,他還有兩個可選參數:blocked和timeout。詳情自行百度
Get方法:從隊列讀取並且刪除一個元素。同樣,他還有兩個可選參數:blocked和timeout。詳情自行百度
from multiprocessing import Process,Queue
sign_done = '__flag_done' #初始化一個變量用來判斷數據
def woke(queue):
for i in range(8):
queue.put(i)
print('往隊列裏面添加數據{}'.format(i))
queue.put(sign_done)
def read(queue):
while 1:
num = queue.get(True)
if num == '__flag_done':
print('down')
break
print('從隊列裏面獲取%s' % num)
if __name__ == '__main__':
duilie = Queue() #構造隊列
t1 = Process(target=woke,args=(duilie,))
t2 = Process(target=read,args=(duilie,))
t1.start()
t2.start()
t1.join()
t2.join()
print('主進程執行完畢')
線程間通信:
線程間通信有兩種方法
1.也可以用Queue
2.event
先看第一種:
from queue import Queue
from threading import Thread
import time,random
shatdown = object()
def witer(out_q):
for num in range(10):
date = random.randint(1,10)
out_q.put(date)
print('添加數據{}'.format(date))
time.sleep(0.2)
out_q.put(shatdown)
def rade(in_q):
while True:
data = in_q.get()
if data is shatdown:
in_q.put(data)
break
else:
print('獲取數據{}'.format(data))
time.sleep(0.3)
q = Queue()
t1 = Thread(target=rade,args=(q,))
t2 = Thread(target=witer,args=(q,))
t1.start()
t2.start()
t1.join()
第二種event:
用 threading.Event 實現線程間通信
使用threading.Event可以使一個線程等待其他線程的通知,我們把這個Event傳遞到線程對象中,
Event默認內置了一個標誌,初始值爲False。
一旦該線程通過wait()方法進入等待狀態,直到另一個線程調用該Event的set()方法將內置標誌設置爲True時,
該Event會通知所有等待狀態的線程恢復運行。
import threading,time
class Myfunc(threading.Thread):
def __init__(self,single,name):
threading.Thread.__init__(self,name = name)
self.single = single
def run(self):
print('我是{}生產線,現在沒錢了,只能停工'.format(self.name))
self.single.wait()
print('籌到錢了,{}開始開工'.format(self.name))
if __name__ == '__main__':
single = threading.Event()
print("這是一個服裝生產線,現在3萬元啓動資金,需要生產3中服裝,但資金不足,只能先開工"
"如果老闆籌不到錢只能將進行中的業務暫時停止,直到有錢!")
for i in ['襯衣','羽絨服','褲子']:
p = Myfunc(single,i)
p.start()
print('老闆籌錢中')
time.sleep(3)
print('老闆籌到錢啦')
single.set() #繼續往下執行
死鎖
所謂死鎖: 是指兩個或兩個以上的線程在執行過程中,因爭奪資源而造成的一種互相等待的現象,
若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。
由於資源佔用是互斥的,當某個進程提出申請資源後,使得有關進程在無外力協助下,永遠分配不到必需的資源而無法繼續運行,這就產生了一種特殊現象死鎖。
1.在多線程中,不可避免的一個問題,就是全局變量資源存在着被多個線程調用的問題,在調用的過程中就存在着資源競爭
2.這種資源競爭是如何產生的呢?
# 舉例: 在一個線程中執行任務需要同時使用兩個以上資源,修改資源時都要獲取對應的資源鎖
A = 0
B = 0
import threading
max_a = threading.Lock() #創建鎖
min_b = threading.Lock() #創建鎖
import threading
class Myfunc(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self): #run方法,只要start就會執行這個方法
self.fun1()
self.fun2()
def fun1(self):
global max_a,min_b
if max_a.acquire(): #鎖定
print('我是線程{},獲取資源{}'.format(self.name,'A'))
if min_b.acquire():
print('我是線程{},獲取資源{}'.format(self.name,'B'))
max_a.release() #釋放資源
def fun2(self):
global max_a,min_b
if min_b.acquire():
print('我是線程{},獲取資源{}'.format(self.name,'B'))
if max_a.acquire():
print('我是線程{},獲取資源{}'.format(self.name, 'A'))
min_b.ralease()
if __name__ == '__main__':
for i in range(100): #循環100次
t1 = Myfunc()
t1.start()
上述代碼,當線程執行次數有限時,全局資源不會發生大的變化,但是當高併發時,就會產生資源競爭問題
嵌套死鎖
如果一個線程遇到鎖嵌套的情況該怎麼辦,這個嵌套是指當我一個線程在獲取臨界資源時,又需要再次獲取。
import threading
import time
#count = threading.RLock()
count = threading.Lock()
a = 0
class Myfunc(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global count,a
if count.acquire(1): #0 就是 False 代表不阻塞 1代表True 代表阻塞
# 默認爲true,會等待,如果是false,就不等待,結果是沒拿到鎖
a+=1
print('{}'.format(self.name,count))
if count.acquire():
a-=1
print('{}'.format(self.name,count))
count.release()
count.release()
if __name__ == '__main__':
for i in range(3):
t1 = Myfunc()
t1.start()
# python提供了“可重入鎖”:threading.RLock。
# 這個RLock內部維護着一個Lock和一個counter變量,counter記錄了acquire的次數,
# 從而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲得資源。
# 使用RLock代替上面例子中的Lock,則不會產生死鎖
線程池
描述:
線程池是維護工作線程池以並行執行耗時操作的對象。
它通過將作業放入工作請求隊列中來爲作業分配作業,
然後由下一個可用的線程拾取它們。然後,它在後臺
執行請求的操作,並將結果放入另一個隊列。
然後,線程池對象可以在它們可用時或在所有線程完成其工作
之後立即從該隊列中的所有線程收集結果。然後可以定
義回調來處理每個結果。
[詳情見官方文檔](https://chrisarndt.de/projects/threadpool/)
優點:
(1)可以控制產生線程的數量。通過預先創建一定數量的工作線程並限制其數量,控制線程對象的內存消耗。
(2)降低系統開銷和資源消耗。通過對多個請求重用線程,線程創建、銷燬的開銷被分攤到了多個請求上。另外通過限制線程數量,降低虛擬機在垃圾回收方面的開銷。
(3)提高系統響應速度。線程事先已被創建,請求到達時可直接進行處理,消除了因線程創建所帶來的延遲,另外多個線程可併發處理。
線程池的基本實現方法:
(1)線程池管理器。創建並維護線程池,根據需要調整池的大小,並監控線程泄漏現象。
(2)工作線程。它是一個可以循環執行任務的線程,沒有任務時處於 Wait 狀態,新任務到達時可被喚醒。
(3)任務隊列。它提供一種緩衝機制,用以臨時存放待處理的任務,同時作爲併發線程的 monitor 對象。
(4)任務接口。它是每個任務必須實現的接口,工作線程通過該接口調度任務的執行。
構建線程池管理器時,首先初始化任務隊列(Queue),運行時通過調用添加任務的方法將任
務添加到任務隊列中。之後創建並啓動一定數量的工作線程,將這些線程保存在線程隊列中。
線程池管理器在運行時可根據需要增加或減少工作線程數量。工作線程運行時首先鎖定任務隊列
,以保證多線程對任務隊列的正確併發訪問,如隊列中有待處理的任務,工作線程取走一個任務
並釋放對任務隊列的鎖定,以便其他線程實現對任務隊列的訪問和處理。在獲取任務之後工作
線程調用任務接口完成對任務的處理。當任務隊列爲空時,工作線程加入到任務隊列的等待線
程列表中,此時工作線程處於 Wait 狀態,幾乎不佔 CPU 資源。一旦新的任務到達,通過調
用任務列表對象的notify方法,從等待線程列表中喚醒一個工作線程以對任務進行處理。通
過這種協作模式,既節省了線程創建、銷燬的開銷,又保證了對任務的併發處理,提高了系統的響應速度。
簡而言之:就是把併發執行的任務傳遞給一個線程池,來替代爲每個併發執行的任務都啓動一個新的線程。
只要池裏有空閒的線程,任務就會分配給一個線程執行。
from multiprocessing.pool import ThreadPool #線程池
def f(n):
print(sum(range(1,1000)))
if __name__ == '__main__':
p = ThreadPool(5) #池的最大進程數
for i in range(5):
p.apply_async(func=f,args=(20,))
p.close() #不在接受新的請求
p.join()