python之進程和線程(二)

進程間通信

進程間通訊有多種方式,包括信號,管道,消息隊列等
進程間通信我們可以用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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章