python 多進程:多進程
先上代碼:
1 pool = threadpool.ThreadPool(10) #建立線程池,控制線程數量為10 2 reqs = threadpool.makeRequests(get_title, data, print_result) #構建請求,get_title為要運行的函數,data為要多線程執行函數的參數 3 #最后這個print_result是可選的,是對前兩個函數運行結果的操作 4 [pool.putRequest(req) for req in reqs] #多線程一塊執行 5 pool.wait() #線程掛起,直到結束
1 [pool.putRequest(req) for req in reqs] #相當於如下兩行代碼 2 for req in requests: 3 pool.putRequest(req)
示例代碼:
1 import threadpool 2 import time,random 3 import Queue 4 5 def hello1(str): 6 time.sleep(2) 7 return str 8 9 def print_ret(request, result): 10 print "the result is %s %r\n" % (request.requestID, result) 11 12 13 def deal_task(pool): 14 try: 15 pool.poll(True) 16 except Exception, e: 17 print str(e) 18 19 #lst = [1,2,3,4,5,6,7] 20 q = Queue.Queue() 21 for i in range(100): 22 q.put(i) 23 24 lst = [q.get() for i in range(q.qsize())] 25 26 pool = threadpool.ThreadPool(20) 27 requests = threadpool.makeRequests(hello1, lst, print_ret) 28 for req in requests: 29 pool.putRequest(req) 30 #deal_task(pool) 31 32 pool.wait()
什么是線程池?
諸如web服務器、數據庫服務器、文件服務器和郵件服務器等許多服務器應用都面向處理來自某些遠程來源的大量短小的任務。構建服務器應用程序的一個過於簡 單的模型是:每當一個請求到達就創建一個新的服務對象,然后在新的服務對象中為請求服務。但當有大量請求並發訪問時,服務器不斷的創建和銷毀對象的開銷很 大。所以提高服務器效率的一個手段就是盡可能減少創建和銷毀對象的次數,特別是一些很耗資源的對象創建和銷毀,這樣就引入了“池”的概念,“池”的概念使 得人們可以定制一定量的資源,然后對這些資源進行復用,而不是頻繁的創建和銷毀。
線程池是預先創建線程的一種技術。線程池在還沒有任務到來之前,創建一定數量的線程,放入空閑隊列中。這些線程都是處於睡眠狀態,即均為啟動,不消耗 CPU,而只是占用較小的內存空間。當請求到來之后,緩沖池給這次請求分配一個空閑線程,把請求傳入此線程中運行,進行處理。當預先創建的線程都處於運行 狀態,即預制線程不夠,線程池可以自由創建一定數量的新線程,用於處理更多的請求。當系統比較閑的時候,也可以通過移除一部分一直處於停用狀態的線程。
線程池的注意事項
雖然線程池是構建多線程應用程序的強大機制,但使用它並不是沒有風險的。在使用線程池時需注意線程池大小與性能的關系,注意並發風險、死鎖、資源不足和線程泄漏等問題。
(1)線程池大小。多線程應用並非線程越多越好,需要根據系統運行的軟硬件環境以及應用本身的特點決定線程池的大小。一般來說,如果代碼結構合理的話,線程數目與CPU 數量相適合即可。如果線程運行時可能出現阻塞現象,可相應增加池的大小;如有必要可采用自適應算法來動態調整線程池的大小,以提高CPU 的有效利用率和系統的整體性能。
(2)並發錯誤。多線程應用要特別注意並發錯誤,要從邏輯上保證程序的正確性,注意避免死鎖現象的發生。
(3)線程泄漏。這是線程池應用中一個嚴重的問題,當任務執行完畢而線程沒能返回池中就會發生線程泄漏現象。
簡單線程池的設計
一個典型的線程池,應該包括如下幾個部分:
1、線程池管理器(ThreadPool),用於啟動、停用,管理線程池
2、工作線程(WorkThread),線程池中的線程
3、請求接口(WorkRequest),創建請求對象,以供工作線程調度任務的執行
4、請求隊列(RequestQueue),用於存放和提取請求
5、結果隊列(ResultQueue),用於存儲請求執行后返回的結果
線程池管理器,通過添加請求的方法(putRequest)向請求隊列(RequestQueue)添加請求,這些請求事先需要實現請求接口,即傳遞工作 函數、參數、結果處理函數、以及異常處理函數。之后初始化一定數量的工作線程,這些線程通過輪詢的方式不斷查看請求隊列(RequestQueue),只 要有請求存在,則會提取出請求,進行執行。然后,線程池管理器調用方法(poll)查看結果隊列(resultQueue)是否有值,如果有值,則取出, 調用結果處理函數執行。通過以上講述,不難發現,這個系統的核心資源在於請求隊列和結果隊列,工作線程通過輪詢requestQueue獲得人物,主線程 通過查看結果隊列,獲得執行結果。因此,對這個隊列的設計,要實現線程同步,以及一定阻塞和超時機制的設計,以防止因為不斷輪詢而導致的過多cpu開銷。
線程池實現原理圖