摘要:多線程的理解多進(jìn)程和多線程都可以執(zhí)行多個任務(wù),線程是進(jìn)程的一部分。多線程創(chuàng)建在中,同樣可以實現(xiàn)多線程,有兩個標(biāo)準(zhǔn)模塊和,不過我們主要使用更高級的模塊。多線程的應(yīng)用場景。
1、多線程的理解
多進(jìn)程和多線程都可以執(zhí)行多個任務(wù),線程是進(jìn)程的一部分。線程的特點是線程之間可以共享內(nèi)存和變量,資源消耗少(不過在Unix環(huán)境中,多進(jìn)程和多線程資源調(diào)度消耗差距不明顯,Unix調(diào)度較快),缺點是線程之間的同步和加鎖比較麻煩。
2、Python多線程創(chuàng)建在Python中,同樣可以實現(xiàn)多線程,有兩個標(biāo)準(zhǔn)模塊thread和threading,不過我們主要使用更高級的threading模塊。使用例子:
import threading import time def target(): print "the curent threading %s is running" % threading.current_thread().name time.sleep(1) print "the curent threading %s is ended" % threading.current_thread().name print "the curent threading %s is running" % threading.current_thread().name t = threading.Thread(target=target) t.start() t.join() print "the curent threading %s is ended" % threading.current_thread().name
輸出:
the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading Thread-1 is ended
the curent threading MainThread is ended
start是啟動線程,join是阻塞當(dāng)前線程,即使得在當(dāng)前線程結(jié)束時,不會退出。從結(jié)果可以看到,主線程直到Thread-1結(jié)束之后才結(jié)束。
Python中,默認(rèn)情況下,如果不加join語句,那么主線程不會等到當(dāng)前線程結(jié)束才結(jié)束,但卻不會立即殺死該線程。如不加join輸出如下:
the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading MainThread is ended
the curent threading Thread-1 is ended
但如果為線程實例添加t.setDaemon(True)之后,如果不加join語句,那么當(dāng)主線程結(jié)束之后,會殺死子線程。代碼:
import threading import time def target(): print "the curent threading %s is running" % threading.current_thread().name time.sleep(4) print "the curent threading %s is ended" % threading.current_thread().name print "the curent threading %s is running" % threading.current_thread().name t = threading.Thread(target=target) t.setDaemon(True) t.start() t.join() print "the curent threading %s is ended" % threading.current_thread().name
輸出如下:
the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading MainThread is ended
如果加上join,并設(shè)置等待時間,就會等待線程一段時間再退出:
import threading import time def target(): print "the curent threading %s is running" % threading.current_thread().name time.sleep(4) print "the curent threading %s is ended" % threading.current_thread().name print "the curent threading %s is running" % threading.current_thread().name t = threading.Thread(target=target) t.setDaemon(True) t.start() t.join(1)
輸出:
the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading MainThread is ended
主線程等待1秒,就自動結(jié)束,并殺死子線程。如果join不加等待時間,t.join(),就會一直等待,一直到子線程結(jié)束,輸出如下:
the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading Thread-1 is ended
the curent threading MainThread is ended
對于多線程來說,最大的特點就是線程之間可以共享數(shù)據(jù),那么共享數(shù)據(jù)就會出現(xiàn)多線程同時更改一個變量,使用同樣的資源,而出現(xiàn)死鎖、數(shù)據(jù)錯亂等情況。
假設(shè)有兩個全局資源,a和b,有兩個線程thread1,thread2. thread1占用a,想訪問b,但此時thread2占用b,想訪問a,兩個線程都不釋放此時擁有的資源,那么就會造成死鎖。
對于該問題,出現(xiàn)了Lock。 當(dāng)訪問某個資源之前,用Lock.acquire()鎖住資源,訪問之后,用Lock.release()釋放資源。
import threading import time a = 3 lock = threading.Lock() def target(): print "the curent threading %s is running" % threading.current_thread().name time.sleep(4) global a #使用global語句可以清楚地表明變量是在外面的塊定義的 lock.acquire() try: a += 3 finally: lock.release() print "the curent threading %s is ended" % threading.current_thread().name print "yes" t = threading.Thread(target=target) t1 = threading.Thread(target=target) t.start() t1.start() t.join() t1.join() print a
用finally的目的是防止當(dāng)前線程無線占用資源。
(2)ThreadLocal介紹完線程鎖,接下來出場的是ThreadLocal。當(dāng)不想將變量共享給其他線程時,可以使用局部變量,但在函數(shù)中定義局部變量會使得在函數(shù)之間傳遞特別麻煩。ThreadLocal是非常牛逼的東西,它解決了全局變量需要枷鎖,局部變量傳遞麻煩的兩個問題。通過在線程中定義:
local_school = threading.local()
此時這個local_school就變成了一個全局變量,但這個全局變量只在該線程中為全局變量,對于其他線程來說是局部變量,別的線程不可更改。 def process_thread(name):# 綁定ThreadLocal的student: local_school.student = name
這個student屬性只有本線程可以修改,別的線程不可以。代碼:
local = threading.local() def func(name): print "current thread:%s" % threading.currentThread().name local.name = name print "%s in %s" % (local.name,threading.currentThread().name) t1 = threading.Thread(target=func,args=("haibo",)) t2 = threading.Thread(target=func,args=("lina",)) t1.start() t2.start() t1.join() t2.join()
從代碼中也可以看到,可以將ThreadLocal理解成一個dict,可以綁定不同變量。
ThreadLocal用的最多的地方就是每一個線程處理一個HTTP請求,在Flask框架中利用的就是該原理,它使用的是基于Werkzeug的LocalStack。
對于多線程的使用,我們經(jīng)常是用thread來創(chuàng)建,比較繁瑣:
class MyThread(threading.Thread): def init(self): threading.Thread.init(self) def run(self): lock.acquire() print threading.currentThread().getName() lock.release() def build_worker(num): workers = [] for t in range(num): work = MyThread() work.start() workers.append(work) return workers def producer(): threads = build_worker(4) for w in threads: w.join() print "Done"
如果要創(chuàng)建更多的線程,那就要一一加到里面,操作麻煩,代碼可讀性也變差。在Python中,可以使用map函數(shù)簡化代碼。map可以實現(xiàn)多任務(wù)的并發(fā),簡單示例:
urls = ["http://www.baidu.com","http://www.sina.com","http://www.qq.com"]
results=map(urllib2.urlopen,urls)
urls = ["http://www.baidu.com","http://www.sina.com","http://www.qq.com"]
results=map(urllib2.urlopen,urls)
map將urls的每個元素當(dāng)做參數(shù)分別傳給urllib2.urlopen函數(shù),并最后把結(jié)果放到results列表中,map 函數(shù)一手包辦了序列操作、參數(shù)傳遞和結(jié)果保存等一系列的操作。 其原理:
map函數(shù)負(fù)責(zé)將線程分給不同的CPU。
在 Python 中有個兩個庫包含了 map 函數(shù): multiprocessing 和它鮮為人知的子庫 multiprocessing.dummy.dummy 是 multiprocessing 模塊的完整克隆,唯一的不同在于 multiprocessing 作用于進(jìn)程,而 dummy 模塊作用于線程。代碼:
import urllib2 from multiprocessing.dummy import Pool as ThreadPool urls = ["http://www.baidu.com","http://www.sina.com","http://www.qq.com"] pool = ThreadPool() results = pool.map(urllib2.urlopen,urls) print results pool.close() pool.join() print "main ended"5、Python多線程的缺陷:
上面說了那么多關(guān)于多線程的用法,但Python多線程并不能真正能發(fā)揮作用,因為在Python中,有一個GIL,即全局解釋鎖,該鎖的存在保證在同一個時間只能有一個線程執(zhí)行任務(wù),也就是多線程并不是真正的并發(fā),只是交替得執(zhí)行。假如有10個線程炮在10核CPU上,當(dāng)前工作的也只能是一個CPU上的線程。
6、Python多線程的應(yīng)用場景。雖然Python多線程有缺陷,總被人說成是雞肋,但也不是一無用處,它很適合用在IO密集型任務(wù)中。I/O密集型執(zhí)行期間大部分是時間都用在I/O上,如數(shù)據(jù)庫I/O,較少時間用在CPU計算上。因此該應(yīng)用場景可以使用Python多線程,當(dāng)一個任務(wù)阻塞在IO操作上時,我們可以立即切換執(zhí)行其他線程上執(zhí)行其他IO操作請求。
總結(jié):Python多線程在IO密集型任務(wù)中還是很有用處的,而對于計算密集型任務(wù),應(yīng)該使用Python多進(jìn)程。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/41027.html
摘要:進(jìn)程可創(chuàng)建多個線程來執(zhí)行同一程序的不同部分。就緒等待線程調(diào)度。運行線程正常運行阻塞暫停運行,解除阻塞后進(jìn)入狀態(tài)重新等待調(diào)度。消亡線程方法執(zhí)行完畢返回或者異常終止。多線程多的情況下,依次執(zhí)行各線程的方法,前頭一個結(jié)束了才能執(zhí)行后面一個。 淺談Python多線程 作者簡介: 姓名:黃志成(小黃)博客: 博客 線程 一.什么是線程? 操作系統(tǒng)原理相關(guān)的書,基本都會提到一句很經(jīng)典的話: 進(jìn)程...
摘要:其次,解釋器的主循環(huán),一個名為的函數(shù),讀取字節(jié)碼并逐個執(zhí)行其中的指令。所有線程都運行相同的代碼,并以相同的方式定期從它們獲取鎖定。無論如何,其他線程無法并行運行。 概述 如今我也是使用Python寫代碼好多年了,但是我卻很少關(guān)心GIL的內(nèi)部機制,導(dǎo)致在寫Python多線程程序的時候。今天我們就來看看CPython的源代碼,探索一下GIL的源碼,了解為什么Python里要存在這個GIL,...
摘要:上一篇文章進(jìn)程專題完結(jié)篇多進(jìn)程處理的一般建議下一篇文章線程專題多線程使用的必要性進(jìn)程線程進(jìn)程能夠完成多任務(wù),比如在一個電腦上可以運行多個軟件。由于占用資源少,也使得多線程程序并發(fā)比較高。 上一篇文章:Python進(jìn)程專題完結(jié)篇:多進(jìn)程處理的一般建議下一篇文章:Python線程專題1:多線程使用的必要性 進(jìn)程VS線程 進(jìn)程:能夠完成多任務(wù),比如在一個電腦上可以運行多個軟件。線程:也能夠...
摘要:也提供多線程支持,而且中的線程并非是模擬出來的多線程,而是系統(tǒng)級別的標(biāo)準(zhǔn)庫提供了兩個模塊和。同一個變量,線程則會互相共享。例如多個線程對銀行中的某一個賬戶進(jìn)行操作。但是實際情況是隨意切換線程。說到的多線程編程,就會繞不過。 該文章參考了http://www.liaoxuefeng.com/wi... 廖雪峰的教程。 一個進(jìn)程至少有一個線程。Python也提供多線程支持,而且Python...
摘要:多線程和鎖作者博客進(jìn)程和線程進(jìn)程是執(zhí)行中的計算機程序。線程包括開始執(zhí)行順序和結(jié)束三部分。的多進(jìn)程相關(guān)模塊模塊是高級別的多線程模塊。線程鎖當(dāng)多線程爭奪鎖時,允許第一個獲得鎖的線程進(jìn)入臨街區(qū),并執(zhí)行代碼。 Python 多線程和鎖 作者博客:http://zzir.cn/ 進(jìn)程和線程 進(jìn)程是執(zhí)行中的計算機程序。每個進(jìn)程都擁有自己的地址空間、內(nèi)存、數(shù)據(jù)棧及其它的輔助數(shù)據(jù)。操作系統(tǒng)管理著所有的...
閱讀 2419·2021-09-30 09:47
閱讀 1405·2021-09-28 09:35
閱讀 3279·2021-09-22 15:57
閱讀 2535·2021-09-22 14:59
閱讀 3676·2021-09-07 10:25
閱讀 3110·2021-09-03 10:48
閱讀 3068·2021-08-26 14:14
閱讀 979·2019-08-30 15:55