python多線程

来源:http://www.cnblogs.com/MrFiona/archive/2016/10/19/5979010.html
-Advertisement-
Play Games

首先,說明一下多線程的應用場景:當python處理多個任務時,這些任務本質是非同步的,需要有多個併發事務,各個事務的運行順序可以是不確定的、隨機的、不可預測的。計算密集型的任務可以順序執行分隔成的多個子任務,也可以用多線程的方式處理。但I/O密集型的任務就不好以單線程方式處理了,如果不用多線程,只能用 ...


首先,說明一下多線程的應用場景:當python處理多個任務時,這些任務本質是非同步的,需要有多個併發事務,各個事務的運行順序可以是不確定的、隨機的、不可預測的。計算密集型的任務可以順序執行分隔成的多個子任務,也可以用多線程的方式處理。但I/O密集型的任務就不好以單線程方式處理了,如果不用多線程,只能用一個或多個計時器來處理實現。

      下麵說一下進程與線程:進程(有時叫重量級進程),是程式的一次執行,正如我們在centos中,ps -aux | grep something 的時候,總有一個他自身產生的進程,也就是這個grep進程,每個進程有自己的地址空間、記憶體、數據棧、及其他記錄其運行軌跡的輔助數據,因此各個進程也不能直接共用信息,只能用進程間通信(IPC)。

      線程(輕量級進程),與進程最大的區別是,所有的線程運行在同一個進程中,共用相同的運行環境,共用同一片數據空間。所以線程之間可以比進程之間更方便的共用數據以及相互通訊,併發執行完成事務。

      為了方便理解記憶進程與線程的關係,我們可以做一個類比:把cpu比作一個搬家公司,而這家搬家公司只有一輛車(進程)來供使用,開始,這家搬家公司很窮,只有一個員工(單線程),那麼,這個搬家公司一天,最多只能搬5家,後來,老闆賺到錢了,他沒買車,而是多雇了n個員工(多線程),這樣,每個員工會被安排每次只搬一家,然後就去休息,把車讓出來,讓其他人搬下一家,這看起來其實並沒有提高多少效率,反而增加了成本是吧,這是因為GIL(Global Interpreter Lock) 全局解釋器鎖,保證了線程安全(保證數據被安全讀取),即同時只能有一個線程在CPU上運行,這是python特有的機制,也就是說,即使你的運行環境具有雙CPU,python虛擬機也只會使用一個cpu,也就是說GIL 直接導致 CPython 不能利用物理多核的性能加速運算。具體的詳細解釋(歷史遺留問題,硬體發展太快)可以參考這篇博客:

      http://blog.sina.com.cn/s/blog_64ecfc2f0102uzzf.html

      在python核心編程中,作者極力建議我們不要使用thread模塊,而是要使用threading模塊,原因如下:

   1、當主線程退出時,所有其他線程沒有被清除就退出了,thread模塊無法保護所有子線程的安全退出。即,thread         模塊不支持守護進程。

   2、thread模塊的屬性有可能會與threading出現衝突。

   3、低級別的thread模塊的同步原語很少(實際只有一個,應該是sleep)。

一、thread模塊 

 以下是不使用GIL和使用GIL的兩個示例代碼:

 1.不使用GIL的代碼示例:

 1 from time import sleep,ctime
 2 import thread
 3 
 4 def loop0():
 5     print 'start loop 0 at: ',ctime()
 6     sleep(4)
 7     print 'loop 0 done at: ',ctime()
 8 def loop1():
 9     print 'start loop 1 at: ',ctime()
10     sleep(2)
11     print 'loop 1 done at: ',ctime()
12 def main():
13     print 'start at: ',ctime()
14     thread.start_new_thread(loop0,())
15     thread.start_new_thread(loop1,())
16     sleep(6)
17     print 'all loop is done, ' ,ctime()
18 
19 if __name__=='__main__':
20     main()
21  
22 
23 輸出結果:
24 
25 start at:  Thu Jan 28 10:46:27 2016
26 start loop 0 at:   Thu Jan 28 10:46:27 2016
27 
28 start loop 1 at:   Thu Jan 28 10:46:27 2016
29 loop 1 done at:  Thu Jan 28 10:46:29 2016
30 loop 0 done at:  Thu Jan 28 10:46:31 2016
31 all loop is done,  Thu Jan 28 10:46:33 2016

由以上輸出可以看出,我們成功開啟了兩個線程,並且與主線程同步,在第2s時,loop1先完成,第4s時loop0完成,又過了2s,主線程完成結束。整個主線程經過了6s,loop0和loop1同步完成。

 

2、使用GIL的代碼示例:

 

 1 import thread
 2 from time import sleep,ctime
 3 loops = [4,2]
 4 def loop(nloop,nsec,lock):
 5     print 'start loop',nloop,'at: ',ctime()
 6     sleep(nsec)
 7     print 'loop',nloop,'done at:',ctime()
 8     lock.release()
 9 def main():
10     print 'starting at:',ctime()
11     locks = []
12     nloops = range(len(loops))
13 
14     for i in nloops:
15         lock = thread.allocate_lock()                          #創建鎖的列表,存在locks中
16         lock.acquire()                         
17         locks.append(lock)                                      
18     for i in nloops:
19         thread.start_new_thread(loop,(i,loops[i],locks[i]))    #創建線程,參數為迴圈號,睡眠時間,鎖
20     for i in nloops:
21         while locks[i].locked():                              #等待迴圈完成,解鎖
22             pass
23     print 'all DONE at:',ctime()
24 if __name__ == '__main__':
25     main()
26  
27 
28 以上輸出如下:
29 
30 starting at: Thu Jan 28 14:59:22 2016
31 start loop  0  at:   Thu Jan 28 14:59:22 2016
32 
33 start loop  1  at:   Thu Jan 28 14:59:22 2016
34 loop 1 done at: Thu Jan 28 14:59:24 2016
35 loop 0 done at: Thu Jan 28 14:59:26 2016
36 all DONE at: Thu Jan 28 14:59:26 2016

 

歷時4秒,這樣效率得到提高,也比在主線程中用一個sleep()函數來計時更為合理。

 

二、threading模塊

1、Thread類

在thread類中,可以用以下三種方法來創建線程:

(1)創建一個thread實例,傳給它一個函數

(2)創建一個thread實例,傳給它一個可調用的類對象

(3)從thread派生出一個子類,創建這個子類的對象

方法(1)

 1 __author__ = 'dell'
 2 import threading
 3 from time import sleep,ctime
 4 def loop0():
 5     print 'start loop 0 at:',ctime()
 6     sleep(4)
 7     print 'loop 0 done at:',ctime()
 8 def loop1():
 9     print 'start loop 1 at:',ctime()
10     sleep(2)
11     print 'loop 1 done at:',ctime()
12 def main():
13     print 'starting at:',ctime()
14     threads = []
15     t1 = threading.Thread(target=loop0,args=())          #創建線程
16     threads.append(t1)
17     t2 = threading.Thread(target=loop1,args=())
18     threads.append(t2)
19     for t in threads:
20         t.setDaemon(True)<span style="white-space:pre">    </span>      #開啟守護線程(一定要在start()前調用)
21         t.start()<span style="white-space:pre">        </span>      #開始線程執行
22     for t in threads:<span style="white-space:pre">                    </span>
23         t.join()<span style="white-space:pre">        </span>      #將程式掛起阻塞,直到線程結束,如果給出數值,則最多阻塞timeout秒
24 
25 if __name__ == '__main__':
26     main()
27     print 'All DONE at:',ctime()
28 
29 在這裡,就不用像thread模塊那樣要管理那麼多鎖(分配、獲取、釋放、檢查等)了,同時我也減少了迴圈的代碼,直接自己編號迴圈了,得到輸出如下:
30  
31 
32 starting at: Thu Jan 28 16:38:14 2016
33 start loop 0 at: Thu Jan 28 16:38:14 2016
34 start loop 1 at: Thu Jan 28 16:38:14 2016
35 loop 1 done at: Thu Jan 28 16:38:16 2016
36 loop 0 done at: Thu Jan 28 16:38:18 2016
37 All DONE at: Thu Jan 28 16:38:18 2016

結果相同,但是從代碼的邏輯來看,要清晰的多了。其他兩種在此就不貼出代碼了。實例化一個Thread與調用thread.start_new_thread直接最大的區別就是新的線程不會立即開始執行,也就是說,在threading模塊的Thread類中當我們實例化之後,再調用.start()函數後被統一執行,這使得我們的程式具有很好的同步特性。

下麵是單線程與多線程的一個對比示例,分別以乘除完成兩組運算,從而看出多線程對效率的提高

 1 from time import ctime,sleep
 2 import threading
 3 
 4 def multi():
 5     num1 = 1
 6     print 'start mutiple at:',ctime()
 7     for i in range(1,10):
 8        num1 = i*num1
 9        sleep(0.2)
10     print 'mutiple finished at:',ctime()
11     return num1
12 def divide():
13     num2 = 100
14     print 'start division at:',ctime()
15     for i in range(1,10):
16         num2 = num2/i
17         sleep(0.4)
18     print 'division finished at:',ctime()
19     return num2
20 def main():
21     print '---->single Thread'
22     x1 = multi()
23     x2 = divide()
24     print 'The sum is ',sum([x1,x2]),'\nfinished singe thread',ctime()
25 
26     print '----->Multi Thread'
27     threads = []
28     t1 = threading.Thread(target=multi,args=())
29     threads.append(t1)
30     t2 = threading.Thread(target=divide,args=())
31     threads.append(t2)
32     for t in threads:
33         t.setDaemon(True)
34         t.start()
35     for t in threads:
36         t.join()
37 
38 if __name__ == '__main__':
39     main()
40 
41 結果如下:
42 
43  
44 
45 ---->single Thread
46 
47 start mutiple at: Thu Jan 28 21:41:18 2016
48 
49 mutiple finished at: Thu Jan 28 21:41:20 2016
50 
51 start division at: Thu Jan 28 21:41:20 2016
52 
53 division finished at: Thu Jan 28 21:41:24 2016
54 
55 The sum is  362880 
56 
57 finished singe thread Thu Jan 28 21:41:24 2016
58 
59 ----->Multi Thread
60 
61 start mutiple at: Thu Jan 28 21:41:24 2016
62 
63 start division at: Thu Jan 28 21:41:24 2016
64 
65 mutiple finished at: Thu Jan 28 21:41:26 2016
66 
67 division finished at: Thu Jan 28 21:41:27 2016
68 
69 The sum is : 362880

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JQuery —— 一個js函數包 一、選擇器 1、基本選擇器 ①id選擇器:# ②class選擇器:. ③標簽名選擇:標簽名 ④併列選擇:用,隔開 ⑤後代選擇:用空格隔開 代碼用法展示: <title></title> <script src="js/jquery-1.7.2.min.js"></ ...
  • 最新用PetaPoco4.0做項目發現有個需求,就是比如說:在mvc表單中,只顯示部分欄位,一個表單還有其他狀態等欄位,沒有顯示到mvc頁面上 但是當MVC收集表單提交更新的時候,會發現會把資料庫中的一些未出現在MVC編輯頁面的欄位更新成null。 為瞭解決這個問題,現在修改下PetaPoco的源碼 ...
  • 上一章筆者介紹了關於WinForm環境。這一章筆者將繼續講WinForm。只不過更加的面向開發了。事實就是在學習工具箱裡面的控制項。對於WinForm開發來講,企業對他的要求並沒有那麼高。但是如果是游戲相關的話,不好意思!筆者覺得你可能選錯語言了。C++可能更合適你。有一點希望讀者們明白。下列講到的內 ...
  • 文檔目錄 本節內容: 配置ABP 替換內置服務 配置模塊 為一個模塊創建配置 替換內置服務 ABP在啟動時,提供基礎框架和模型來配置和模塊化。 置ABP 在預初始化事件中進行配置,示例: ABP進行了模塊化設計,不同的模塊都能配置ABP。例如,不同的模塊都能添加導航提供器把自己的菜單項加入到主菜單中 ...
  • 一、基本選擇器: ID選擇器:$("#id名稱") 例如:$("#div1").css("width","100px"); CLASS選擇器:$(".id名稱") 併列$("#id名稱,#id名稱") 後代$("#id名稱 名稱")二.過濾選擇器 第一個:$(".id名稱:first") 最後一個: ...
  • 《深入理解Java虛擬機》第二三章摘要 Java記憶體區域與記憶體溢出 Java虛擬機中的記憶體分配圖: 各個區域的特性總結如下表: 補充說明: 當多線程情形下,可能多個線程要在堆上分配記憶體,那麼可能出現記憶體分配的同步問題,解決方案有兩個,一個就是同步記憶體分配動作;另一個就是採用TLAB,即在Java堆中 ...
  • 英文文檔: Return a Boolean value, i.e. one of True or False. x is converted using the standard truth testing procedure. If x is false or omitted, this ret ...
  • 一.在java中提供的一些修飾符,這些修飾符可以修飾類、變數和方法,在java中常見的修飾符有:abstract(抽象的)、static(靜態的)、public(公共的)、protected(受保護的)、private(私有的)、synchronized(同步的)、native(本地的)、trans ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...