python線程銷毀-九游会j9娱乐平台
a. 小白都看懂了,python 中的線程和進程精講,建議收藏
目錄
眾所周知,cpu是計算機的核心,它承擔了所有的計算任務。而操作系統是計算機的管理者,是一個大管家,它負責任務的調度,資源的分配和管理,統領整個計算機硬體。應用程序是具有某種功能的程序,程序運行與操作系統之上
在很早的時候計算機並沒有線程這個概念,但是隨著時代的發展,只用進程來處理程序出現很多的不足。如當一個進程堵塞時,整個程序會停止在堵塞處,並且如果頻繁的切換進程,會浪費系統資源。所以線程出現了
線程是能擁有資源和獨立運行的最小單位,也是程序執行的最小單位。一個進程可以擁有多個線程,而且屬於同一個進程的多個線程間會共享該進行的資源
① 200 多本 python 電子書(和經典的書籍)應該有
② python標准庫資料(最全中文版)
③ 項目源碼(四五十個有趣且可靠的練手項目及源碼)
④ python基礎入門、爬蟲、網路開發、大數據分析方面的視頻(適合小白學習)
⑤ python學習路線圖(告別不入流的學習)
私信我01即可獲取大量python學習資源
進程時一個具有一定功能的程序在一個數據集上的一次動態執行過程。進程由程序,數據集合和進程式控制制塊三部分組成。程序用於描述進程要完成的功能,是控制進程執行的指令集;數據集合是程序在執行時需要的數據和工作區;程序控制塊(pcb)包含程序的描述信息和控制信息,是進程存在的唯一標志
在python中,通過兩個標准庫 thread 和 threading 提供對線程的支持, threading 對 thread 進行了封裝。 threading 模塊中提供了 thread , lock , rlock , condition 等組件
在python中線程和進程的使用就是通過 thread 這個類。這個類在我們的 thread 和 threading 模塊中。我們一般通過 threading 導入
默認情況下,只要在解釋器中,如果沒有報錯,則說明線程可用
守護模式:
現在我們程序代碼中,有多個線程, 並且在這個幾個線程中都會去 操作同一部分內容,那麼如何實現這些數據的共享呢?
這時,可以使用 threading庫裡面的鎖對象 lock 去保護
lock 對象的acquire方法 是申請鎖
每個線程在操作共享數據對象之前,都應該申請獲取操作權,也就是調用該共享數據對象對應的鎖對象的acquire方法,如果線程a 執行了 acquire() 方法,別的線程b 已經申請到了這個鎖, 並且還沒有釋放,那麼 線程a的代碼就在此處 等待 線程b 釋放鎖,不去執行後面的代碼。
直到線程b 執行了鎖的 release 方法釋放了這個鎖, 線程a 才可以獲取這個鎖,就可以執行下面的代碼了
如:
到在使用多線程時,如果數據出現和自己預期不符的問題,就可以考慮是否是共享的數據被調用覆蓋的問題
使用 threading 庫裡面的鎖對象 lock 去保護
python中的多進程是通過multiprocessing包來實現的,和多線程的threading.thread差不多,它可以利用multiprocessing.process對象來創建一個進程對象。這個進程對象的方法和線程對象的方法差不多也有start(), run(), join()等方法,其中有一個方法不同thread線程對象中的守護線程方法是setdeamon,而process進程對象的守護進程是通過設置daemon屬性來完成的
守護模式:
其使用方法和線程的那個 lock 使用方法類似
manager的作用是提供多進程共享的全局變數,manager()方法會返回一個對象,該對象控制著一個服務進程,該進程中保存的對象運行其他進程使用代理進行操作
語法:
線程池的基類是 concurrent.futures 模塊中的 executor , executor 提供了兩個子類,即 threadpoolexecutor 和 processpoolexecutor ,其中 threadpoolexecutor 用於創建線程池,而 processpoolexecutor 用於創建進程池
如果使用線程池/進程池來管理並發編程,那麼只要將相應的 task 函數提交給線程池/進程池,剩下的事情就由線程池/進程池來搞定
exectuor 提供了如下常用方法:
程序將 task 函數提交(submit)給線程池後,submit 方法會返回一個 future 對象,future 類主要用於獲取線程任務函數的返回值。由於線程任務會在新線程中以非同步方式執行,因此,線程執行的函數相當於一個「將來完成」的任務,所以 python 使用 future 來代表
future 提供了如下方法:
使用線程池來執行線程任務的步驟如下:
最佳線程數目 = ((線程等待時間 線程cpu時間)/線程cpu時間 )* cpu數目
也可以低於 cpu 核心數
使用線程池來執行線程任務的步驟如下:
關於進程的開啟代碼一定要放在 if __name__ == '__main__': 代碼之下,不能放到函數中或其他地方
開啟進程的技巧
開啟進程的數量最好低於最大 cpu 核心數
b. python 主程序結束的時候線程是否結束問題2個問題
第一個問題:因為主進程已經結束,相關的資源已經釋放,而線程還在後台運行,所以會導致線程找不到相關的資源和定義
第二個問題:因為主程序結束的時候,並沒有等待子線程結束,也沒有強制關閉子線程,因此還在後台運行,有兩個辦法可以讓他們同時結束,一個辦法是在在構建進程的時候增加參數 deamon=true, 第二個辦法就是在程序最後增加thread1.join(),thread2.join()
c. python線程怎麼銷毀
【python】線程的創建、執行、互斥、同步、銷毀
還是《【java】利用synchronized(this)完成線程的臨界區》(點擊打開鏈接)、《【linux】線程互斥》(點擊打開鏈接)、《【c 】windows線程的創建、執行、互斥、同步、銷毀》(點擊打開鏈接)中的設置多個線程對一個ticket進行自減操作,用來說明python中多線程的運用,涉及的創建、執行、互斥、同步、銷毀問題。
運行結果如下,還是差不多,運行三次,每次的運行結果,每個線程最終的得票結果是不同的,但是4個線程最終「得票」的總和為 ticket 最初設置的值為100000,證明這4個線程成功實現了互斥。
雖然每次運行結果是不同,但是可以看得出每次運行結果大抵上是平均的。貌似python對線程作系統資源的處理,比java要好。
然而,python總要實現多線程,代碼並不像想像中簡單,具體如下:
[python] view plain print?在code上查看代碼片派生到我的代碼片
# -*-coding:utf-8-*-
import threading;
mutex_lock = threading.rlock(); # 互斥鎖的聲明
ticket = 100000; # 總票數
# 用於統計各個線程的得票數
ticket_for_thread1 = 0;
ticket_for_thread2 = 0;
ticket_for_thread3 = 0;
ticket_for_thread4 = 0;
class mythread(threading.thread): # 線程處理函數
def __init__(self, name):
threading.thread.__init__(self); # 線程類必須的初始化
self.thread_name = name; # 將傳遞過來的name構造到類中的name
def run(self):
# 聲明在類中使用全局變數
global mutex_lock;
global ticket;
global ticket_for_thread1;
global ticket_for_thread2;
global ticket_for_thread3;
global ticket_for_thread4;
while 1:
mutex_lock.acquire(); # 臨界區開始,互斥的開始
# 僅能有一個線程↓↓↓↓↓↓↓↓↓↓↓↓
if ticket > 0:
ticket -= 1;
# 統計哪到線程拿到票
print "%s搶到了票!票還剩餘:%d。" % (self.thread_name, ticket);
if self.thread_name == "線程1":
ticket_for_thread1 = 1;
elif self.thread_name == "線程2":
ticket_for_thread2 = 1;
elif self.thread_name == "線程3":
ticket_for_thread3 = 1;
elif self.thread_name == "線程4":
ticket_for_thread4 = 1;
else:
break;
# 僅能有一個線程↑↑↑↑↑↑↑↑↑↑↑↑
mutex_lock.release(); # 臨界區結束,互斥的結束
mutex_lock.release(); # python在線程死亡的時候,不會清理已存在在線程函數的互斥鎖,必須程序猿自己主動清理
print "%s被銷毀了!" % (self.thread_name);
# 初始化線程
thread1 = mythread("線程1");
thread2 = mythread("線程2");
thread3 = mythread("線程3");
thread4 = mythread("線程4");
# 開啟線程
thread1.start();
thread2.start();
thread3.start();
thread4.start();
# 等到線程1、2、3、4結束才進行以下的代碼(同步)
thread1.join();
thread2.join();
thread3.join();
thread4.join();
print "票都搶光了,大家都散了吧!";
print "=========得票統計=========";
print "線程1:%d張" % (ticket_for_thread1);
print "線程2:%d張" % (ticket_for_thread2);
print "線程3:%d張" % (ticket_for_thread3);
print "線程4:%d張" % (ticket_for_thread4);
1、從上面的代碼可以看出,在python2.7中要使用線程必須使用threading而不是古老的thread模塊。
如果你像網上部分遺留依舊的文章一樣,在python2.7中使用thread來實現線程,至少在eclipse的pydev中會報錯:sys.excepthook is missing,lost sys.stderr如下圖所示:
所以必須使用現時python建議使用的threading。
2、與其它編程語言類似,聲明一個互斥鎖,與一系列的得票數。之後,與java同樣地,python實現線程的函數,是要重寫一個類。而類中使用全局變數,則與同為腳本語言的php一樣《【php】global的使用與php的全局變數》(點擊打開鏈接),要用global才能使用這個全局變數,而不是c/c 可以直接使用。
3、需要注意的,python需要在線程跑完class mythread(threading.thread)這個類的def run(self)方法之前,必須自己手動清理互斥鎖,它不會像其它編程語言那樣,說線程跑完def run(self)方法,會自然而然地清理該線程被創建的互斥鎖。如果沒有最後一句手動清理互斥鎖,則會造成死鎖。
4、最後與其它編程語言一樣了,利用線程的join方法可以等待這個線程跑完def run(self)方法中的所有代碼,才執行之後的代碼,實現同步。否則主函數中的代碼,相當於與父線程。主函數開啟的線程,相當於其子線程,互不影響的。
d. 為什麼在python里推薦使用多進程而不是多線程
首先強調背景:
1. gil是什麼?
gil的全稱是global interpreter lock(全局解釋器鎖),來源是python設計之初的考慮,為了數據安全所做的決定。
2. 每個cpu在同一時間只能執行一個線程
在單核cpu下的多線程其實都只是並發,不是並行,並發和並行從宏觀上來講都是同時處理多路請求的概念。但並發和並行又有區別,並行是指兩個或者多個事件在同一時刻發生;而並發是指兩個或多個事件在同一時間間隔內發生。
在python多線程下,每個線程的執行方式:
獲取gil
執行代碼直到sleep或者是python虛擬機將其掛起。
釋放gil
cpu密集型代碼(各種循環處理、計數等等),在這種情況下,由於計算工作多,ticks計數很快就會達到閾值,然後觸發gil的釋放與再競爭(多個線程來回切換當然是需要消耗資源的),所以python下的多線程對cpu密集型代碼並不友好。
io密集型代碼(文件處理、網路爬蟲等),多線程能夠有效提升效率(單線程下有io操作會進行io等待,造成不必要的時間浪費,而開啟多線程能在線程a等待時,自動切換到線程b,可以不浪費cpu的資源,從而能提升程序執行效率)。所以python的多線程對io密集型代碼比較友好。
可見,某個線程想要執行,必須先拿到gil,我們可以把gil看作是「通行證」,並且在一個python進程中,gil只有一個。拿不到通行證的線程,就不允許進入cpu執行。
在python2.x里,gil的釋放邏輯是當前線程遇見io操作或者ticks計數達到100(ticks可以看作是python自身的一個計數器,專門作用於gil,每次釋放後歸零,這個計數可以通過 sys.setcheckinterval 來調整),進行釋放。
而每次釋放gil鎖,線程進行鎖競爭、切換線程,會消耗資源。並且由於gil鎖存在,python里一個進程永遠只能同時執行一個線程(拿到gil的線程才能執行),這就是為什麼在多核cpu上,python的多線程效率並不高。
那麼是不是python的多線程就完全沒用了呢?
在這里我們進行分類討論:
而在python3.x中,gil不使用ticks計數,改為使用計時器(執行時間達到閾值後,當前線程釋放gil),這樣對cpu密集型程序更加友好,但依然沒有解決gil導致的同一時間只能執行一個線程的問題,所以效率依然不盡如人意。
請注意:多核多線程比單核多線程更差,原因是單核下的多線程,每次釋放gil,喚醒的那個線程都能獲取到gil鎖,所以能夠無縫執行,但多核下,cpu0釋放gil後,其他cpu上的線程都會進行競爭,但gil可能會馬上又被cpu0拿到,導致其他幾個cpu上被喚醒後的線程會醒著等待到切換時間後又進入待調度狀態,這樣會造成線程顛簸(thrashing),導致效率更低。
回到最開始的問題:經常我們會聽到老手說:「python下想要充分利用多核cpu,就用多進程」,原因是什麼呢?
原因是:每個進程有各自獨立的gil,互不幹擾,這樣就可以真正意義上的並行執行,所以在python中,多進程的執行效率優於多線程(僅僅針對多核cpu而言)。
所以在這里說結論:多核下,想做並行提升效率,比較通用的方法是使用多進程,能夠有效提高執行效率
e. python線程怎麼銷毀
可以新建一個線程作為父線程,然後實際工作是在它的一個子線程裡面做,父線程循環檢測一個變數來決定是否退出。talk
is
cheap
import
threading
class
testthread(threading.thread):
def
__init__(self,
thread_num=0,
timeout=1.0):
super(testthread,
self).__init__()
self.thread_num
=
thread_num
self.stopped
=
false
self.timeout
=
timeout
def
run(self):
def
target_func():
inp
=
raw_input("thread
%d:
"
%
self.thread_num)
print('thread
%s
input
%s'
%
(self.thread_num,
inp))
subthread
=
threading.thread(target=target_func,
args=())
subthread.setdaemon(true)
subthread.start()
while
not
self.stopped:
subthread.join(self.timeout)
print('thread
stopped')
def
stop(self):
self.stopped
=
true
def
isstopped(self):
return
self.stopped
thread
=
testthread()
thread.start()
import
time
print('main
thread
wainting')
time.sleep(2)
thread.stop()
thread.join()
f. 一篇文章帶你深度解析python線程和進程
使用python中的線程模塊,能夠同時運行程序的不同部分,並簡化設計。如果你已經入門python,並且想用線程來提升程序運行速度的話,希望這篇教程會對你有所幫助。
線程與進程
什麼是進程
進程是系統進行資源分配和調度的一個獨立單位 進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。每個進程都有自己的獨立內存空間,不同進程通過進程間通信來通信。由於進程比較重量,占據獨立的內存,所以上下文進程間的切換開銷(棧、寄存器、虛擬內存、文件句柄等)比較大,但相對比較穩定安全。
什麼是線程
cpu調度和分派的基本單位 線程是進程的一個實體,是cpu調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。線程間通信主要通過共享內存,上下文切換很快,資源開銷較少,但相比進程不夠穩定容易丟失數據。
進程與線程的關系圖
線程與進程的區別:
進程
現實生活中,有很多的場景中的事情是同時進行的,比如開車的時候 手和腳共同來駕駛 汽車 ,比如唱歌跳舞也是同時進行的,再比如邊吃飯邊打電話;試想如果我們吃飯的時候有一個領導來電,我們肯定是立刻就接聽了。但是如果你吃完飯再接聽或者回電話,很可能會被開除。
注意:
多任務的概念
什麼叫 多任務 呢?簡單地說,就是操作系統可以同時運行多個任務。打個比方,你一邊在用瀏覽器上網,一邊在聽mp3,一邊在用word趕作業,這就是多任務,至少同時有3個任務正在運行。還有很多任務悄悄地在後台同時運行著,只是桌面上沒有顯示而已。
現在,多核cpu已經非常普及了,但是,即使過去的單核cpu,也可以執行多任務。由於cpu執行代碼都是順序執行的,那麼,單核cpu是怎麼執行多任務的呢?
答案就是操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒,這樣反復執行下去。表面上看,每個任務都是交替執行的,但是,由於cpu的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。
真正的並行執行多任務只能在多核cpu上實現,但是,由於任務數量遠遠多於cpu的核心數量,所以,操作系統也會自動把很多任務輪流調度到每個核心上執行。 其實就是cpu執行速度太快啦!以至於我們感受不到在輪流調度。
並行與並發
並行(parallelism)
並行:指兩個或兩個以上事件(或線程)在同一時刻發生,是真正意義上的不同事件或線程在同一時刻,在不同cpu資源呢上(多核),同時執行。
特點
並發(concurrency)
指一個物理cpu(也可以多個物理cpu) 在若幹道程序(或線程)之間多路復用,並發性是對有限物理資源強制行使多用戶共享以提高效率。
特點
multiprocess.process模塊
process模塊是一個創建進程的模塊,藉助這個模塊,就可以完成進程的創建。
語法:process([group [, target [, name [, args [, kwargs]]]]])
由該類實例化得到的對象,表示一個子進程中的任務(尚未啟動)。
注意:1. 必須使用關鍵字方式來指定參數;2. args指定的為傳給target函數的位置參數,是一個元祖形式,必須有逗號。
參數介紹:
group:參數未使用,默認值為none。
target:表示調用對象,即子進程要執行的任務。
args:表示調用的位置參數元祖。
kwargs:表示調用對象的字典。如kwargs = {'name':jack, 'age':18}。
name:子進程名稱。
代碼:
除了上面這些開啟進程的方法之外,還有一種以繼承process的方式開啟進程的方式:
通過上面的研究,我們千方百計實現了程序的非同步,讓多個任務可以同時在幾個進程中並發處理,他們之間的運行沒有順序,一旦開啟也不受我們控制。盡管並發編程讓我們能更加充分的利用io資源,但是也給我們帶來了新的問題。
當多個進程使用同一份數據資源的時候,就會引發數據安全或順序混亂問題,我們可以考慮加鎖,我們以模擬搶票為例,來看看數據安全的重要性。
加鎖可以保證多個進程修改同一塊數據時,同一時間只能有一個任務可以進行修改,即串列的修改。加鎖犧牲了速度,但是卻保證了數據的安全。
因此我們最好找尋一種解決方案能夠兼顧:1、效率高(多個進程共享一塊內存的數據)2、幫我們處理好鎖問題。
mutiprocessing模塊為我們提供的基於消息的ipc通信機制:隊列和管道。隊列和管道都是將數據存放於內存中 隊列又是基於(管道 鎖)實現的,可以讓我們從復雜的鎖問題中解脫出來, 我們應該盡量避免使用共享數據,盡可能使用消息傳遞和隊列,避免處理復雜的同步和鎖問題,而且在進程數目增多時,往往可以獲得更好的可獲展性( 後續擴展該內容 )。
線程
python的threading模塊
python 供了幾個用於多線程編程的模塊,包括 thread, threading 和 queue 等。thread 和 threading 模塊允許程序員創建和管理線程。thread 模塊 供了基本的線程和鎖的支持,而 threading 供了更高級別,功能更強的線程管理的功能。queue 模塊允許用戶創建一個可以用於多個線程之間 共享數據的隊列數據結構。
python創建和執行線程
創建線程代碼
1. 創建方法一:
2. 創建方法二:
進程和線程都是實現多任務的一種方式,例如:在同一台計算機上能同時運行多個qq(進程),一個qq可以打開多個聊天窗口(線程)。資源共享:進程不能共享資源,而線程共享所在進程的地址空間和其他資源,同時,線程有自己的棧和棧指針。所以在一個進程內的所有線程共享全局變數,但多線程對全局變數的更改會導致變數值得混亂。
代碼演示:
得到的結果是:
首先需要明確的一點是gil並不是python的特性,它是在實現python解析器(cpython)時所引入的一個概念。就好比c 是一套語言(語法)標准,但是可以用不同的編譯器來編譯成可執行代碼。同樣一段代碼可以通過cpython,pypy,psyco等不同的python執行環境來執行(其中的jpython就沒有gil)。
那麼cpython實現中的gil又是什麼呢?gil全稱global interpreter lock為了避免誤導,我們還是來看一下官方給出的解釋:
主要意思為:
因此,解釋器實際上被一個全局解釋器鎖保護著,它確保任何時候都只有一個python線程執行。在多線程環境中,python 虛擬機按以下方式執行:
由於gil的存在,python的多線程不能稱之為嚴格的多線程。因為 多線程下每個線程在執行的過程中都需要先獲取gil,保證同一時刻只有一個線程在運行。
由於gil的存在,即使是多線程,事實上同一時刻只能保證一個線程在運行, 既然這樣多線程的運行效率不就和單線程一樣了嗎,那為什麼還要使用多線程呢?
由於以前的電腦基本都是單核cpu,多線程和單線程幾乎看不出差別,可是由於計算機的迅速發展,現在的電腦幾乎都是多核cpu了,最少也是兩個核心數的,這時差別就出來了:通過之前的案例我們已經知道,即使在多核cpu中,多線程同一時刻也只有一個線程在運行,這樣不僅不能利用多核cpu的優勢,反而由於每個線程在多個cpu上是交替執行的,導致在不同cpu上切換時造成資源的浪費,反而會更慢。即原因是一個進程只存在一把gil鎖,當在執行多個線程時,內部會爭搶gil鎖,這會造成當某一個線程沒有搶到鎖的時候會讓cpu等待,進而不能合理利用多核cpu資源。
但是在使用多線程抓取網頁內容時,遇到io阻塞時,正在執行的線程會暫時釋放gil鎖,這時其它線程會利用這個空隙時間,執行自己的代碼,因此多線程抓取比單線程抓取性能要好,所以我們還是要使用多線程的。
gil對多線程python程序的影響
程序的性能受到計算密集型(cpu)的程序限制和i/o密集型的程序限制影響,那什麼是計算密集型和i/o密集型程序呢?
計算密集型:要進行大量的數值計算,例如進行上億的數字計算、計算圓周率、對視頻進行高清解碼等等。這種計算密集型任務雖然也可以用多任務完成,但是花費的主要時間在任務切換的時間,此時cpu執行任務的效率比較低。
io密集型:涉及到網路請求(time.sleep())、磁碟io的任務都是io密集型任務,這類任務的特點是cpu消耗很少,任務的大部分時間都在等待io操作完成(因為io的速度遠遠低於cpu和內存的速度)。對於io密集型任務,任務越多,cpu效率越高,但也有一個限度。
當然為了避免gil對我們程序產生影響,我們也可以使用,線程鎖。
lock&rlock
常用的資源共享鎖機制:有lock、rlock、semphore、condition等,簡單給大家分享下lock和rlock。
lock
特點就是執行速度慢,但是保證了數據的安全性
rlock
使用鎖代碼操作不當就會產生死鎖的情況。
什麼是死鎖
死鎖:當線程a持有獨占鎖a,並嘗試去獲取獨占鎖b的同時,線程b持有獨占鎖b,並嘗試獲取獨占鎖a的情況下,就會發生ab兩個線程由於互相持有對方需要的鎖,而發生的阻塞現象,我們稱為死鎖。即死鎖是指多個進程因競爭資源而造成的一種僵局,若無外力作用,這些進程都將無法向前推進。
所以,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確定資源的合理分配演算法,避免進程永久占據系統資源。
死鎖代碼
python線程間通信
如果各個線程之間各干各的,確實不需要通信,這樣的代碼也十分的簡單。但這一般是不可能的,至少線程要和主線程進行通信,不然計算結果等內容無法取回。而實際情況中要復雜的多,多個線程間需要交換數據,才能得到正確的執行結果。
python中queue是消息隊列,提供線程間通信機制,python3中重名為為queue,queue模塊塊下提供了幾個阻塞隊列,這些隊列主要用於實現線程通信。
在 queue 模塊下主要提供了三個類,分別代表三種隊列,它們的主要區別就在於進隊列、出隊列的不同。
簡單代碼演示
此時代碼會阻塞,因為queue中內容已滿,此時可以在第四個queue.put('蘋果')後面添加timeout,則成為 queue.put('蘋果',timeout=1)如果等待1秒鍾仍然是滿的就會拋出異常,可以捕獲異常。
同理如果隊列是空的,無法獲取到內容默認也會阻塞,如果不阻塞可以使用queue.get_nowait()。
在掌握了 queue 阻塞隊列的特性之後,在下面程序中就可以利用 queue 來實現線程通信了。
下面演示一個生產者和一個消費者,當然都可以多個
使用queue模塊,可在線程間進行通信,並保證了線程安全。
協程
協程,又稱微線程,纖程。英文名coroutine。
協程是python個中另外一種實現多任務的方式,只不過比線程更小佔用更小執行單元(理解為需要的資源)。為啥說它是一個執行單元,因為它自帶cpu上下文。這樣只要在合適的時機, 我們可以把一個協程 切換到另一個協程。只要這個過程中保存或恢復 cpu上下文那麼程序還是可以運行的。
通俗的理解:在一個線程中的某個函數,可以在任何地方保存當前函數的一些臨時變數等信息,然後切換到另外一個函數中執行,注意不是通過調用函數的方式做到的,並且切換的次數以及什麼時候再切換到原來的函數都由開發者自己確定。
在實現多任務時,線程切換從系統層面遠不止保存和恢復 cpu上下文這么簡單。操作系統為了程序運行的高效性每個線程都有自己緩存cache等等數據,操作系統還會幫你做這些數據的恢復操作。所以線程的切換非常耗性能。但是協程的切換只是單純的操作cpu的上下文,所以一秒鍾切換個上百萬次系統都抗的住。
greenlet與gevent
為了更好使用協程來完成多任務,除了使用原生的yield完成模擬協程的工作,其實python還有的greenlet模塊和gevent模塊,使實現協程變的更加簡單高效。
greenlet雖說實現了協程,但需要我們手工切換,太麻煩了,gevent是比greenlet更強大的並且能夠自動切換任務的模塊。
其原理是當一個greenlet遇到io(指的是input output 輸入輸出,比如網路、文件操作等)操作時,比如訪問網路,就自動切換到其他的greenlet,等到io操作完成,再在適當的時候切換回來繼續執行。
模擬耗時操作:
如果有耗時操作也可以換成,gevent中自己實現的模塊,這時候就需要打補丁了。
使用協程完成一個簡單的二手房信息的爬蟲代碼吧!
以下文章來源於python專欄 ,作者宋宋
文章鏈接:https://mp.weixin.qq.com/s/2r3_ipu3hjda5vnqshjunq