詳解Python多線程
本文實(shí)例為大家解析了Python多線程,供大家參考,具體內(nèi)容如下
1、多線程的理解
多進(jìn)程和多線程都可以執(zhí)行多個(gè)任務(wù),線程是進(jìn)程的一部分。線程的特點(diǎn)是線程之間可以共享內(nèi)存和變量,資源消耗少(不過在Unix環(huán)境中,多進(jìn)程和多線程資源調(diào)度消耗差距不明顯,Unix調(diào)度較快),缺點(diǎn)是線程之間的同步和加鎖比較麻煩。
2、Python多線程創(chuàng)建
在Python中,同樣可以實(shí)現(xiàn)多線程,有兩個(gè)標(biāo)準(zhǔn)模塊thread和threading,不過我們主要使用更高級(jí)的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是啟動(dòng)線程,join是阻塞當(dāng)前線程,即使得在當(dāng)前線程結(jié)束時(shí),不會(huì)退出。從結(jié)果可以看到,主線程直到Thread-1結(jié)束之后才結(jié)束。
Python中,默認(rèn)情況下,如果不加join語句,那么主線程不會(huì)等到當(dāng)前線程結(jié)束才結(jié)束,但卻不會(huì)立即殺死該線程。如不加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
但如果為線程實(shí)例添加t.setDaemon(True)之后,如果不加join語句,那么當(dāng)主線程結(jié)束之后,會(huì)殺死子線程。代碼:
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 runningthe curent threading MainThread is ended
如果加上join,并設(shè)置等待時(shí)間,就會(huì)等待線程一段時(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秒,就自動(dòng)結(jié)束,并殺死子線程。如果join不加等待時(shí)間,t.join(),就會(huì)一直等待,一直到子線程結(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
3、線程鎖和ThreadLocal
(1)線程鎖
對(duì)于多線程來說,最大的特點(diǎn)就是線程之間可以共享數(shù)據(jù),那么共享數(shù)據(jù)就會(huì)出現(xiàn)多線程同時(shí)更改一個(gè)變量,使用同樣的資源,而出現(xiàn)死鎖、數(shù)據(jù)錯(cuò)亂等情況。
假設(shè)有兩個(gè)全局資源,a和b,有兩個(gè)線程thread1,thread2. thread1占用a,想訪問b,但此時(shí)thread2占用b,想訪問a,兩個(gè)線程都不釋放此時(shí)擁有的資源,那么就會(huì)造成死鎖。
對(duì)于該問題,出現(xiàn)了Lock。 當(dāng)訪問某個(gè)資源之前,用Lock.acquire()鎖住資源,訪問之后,用Lock.release()釋放資源。
a = 3 lock = threading.Lock() def target(): print 'the curent threading %s is running' % threading.current_thread().name time.sleep(4) global a lock.acquire() try: a += 3 finally: lock.release() print 'the curent threading %s is ended' % threading.current_thread().name print 'yes'
用finally的目的是防止當(dāng)前線程無線占用資源。
(2)ThreadLocal
介紹完線程鎖,接下來出場(chǎng)的是ThreadLocal。當(dāng)不想將變量共享給其他線程時(shí),可以使用局部變量,但在函數(shù)中定義局部變量會(huì)使得在函數(shù)之間傳遞特別麻煩。ThreadLocal是非常牛逼的東西,它解決了全局變量需要枷鎖,局部變量傳遞麻煩的兩個(gè)問題。通過在線程中定義:
local_school = threading.local()
此時(shí)這個(gè)local_school就變成了一個(gè)全局變量,但這個(gè)全局變量只在該線程中為全局變量,對(duì)于其他線程來說是局部變量,別的線程不可更改。 def process_thread(name):# 綁定ThreadLocal的student: local_school.student = name
這個(gè)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理解成一個(gè)dict,可以綁定不同變量。
ThreadLocal用的最多的地方就是每一個(gè)線程處理一個(gè)HTTP請(qǐng)求,在Flask框架中利用的就是該原理,它使用的是基于Werkzeug的LocalStack。
4、Map實(shí)現(xiàn)多線程
對(duì)于多線程的使用,我們經(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'
如果要?jiǎng)?chuàng)建更多的線程,那就要一一加到里面,操作麻煩,代碼可讀性也變差。在Python中,可以使用map函數(shù)簡(jiǎn)化代碼。map可以實(shí)現(xiàn)多任務(wù)的并發(fā),簡(jiǎn)單示例:
results=map(urllib2.urlopen,urls)
map將urls的每個(gè)元素當(dāng)做參數(shù)分別傳給urllib2.urlopen函數(shù),并最后把結(jié)果放到results列表中,map 函數(shù)一手包辦了序列操作、參數(shù)傳遞和結(jié)果保存等一系列的操作。 其原理:
map函數(shù)負(fù)責(zé)將線程分給不同的CPU。
在 Python 中有個(gè)兩個(gè)庫(kù)包含了 map 函數(shù): multiprocessing 和它鮮為人知的子庫(kù) 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'
pool = ThreadPool()創(chuàng)建了線程池,其默認(rèn)值為當(dāng)前機(jī)器 CPU 的核數(shù),可以指定線程池大小,不是越多越好,因?yàn)樵蕉嗟脑?,線程之間的切換也是很消耗資源的。
results = pool.map(urllib2.urlopen,urls) 該語句將不同的url傳給各自的線程,并把執(zhí)行后結(jié)果返回到results中。
代碼清晰明了,巧妙得完成Threading模塊完成的功能。
5、Python多線程的缺陷:
上面說了那么多關(guān)于多線程的用法,但Python多線程并不能真正能發(fā)揮作用,因?yàn)樵赑ython中,有一個(gè)GIL,即全局解釋鎖,該鎖的存在保證在同一個(gè)時(shí)間只能有一個(gè)線程執(zhí)行任務(wù),也就是多線程并不是真正的并發(fā),只是交替得執(zhí)行。假如有10個(gè)線程炮在10核CPU上,當(dāng)前工作的也只能是一個(gè)CPU上的線程。
6、Python多線程的應(yīng)用場(chǎng)景。
雖然Python多線程有缺陷,總被人說成是雞肋,但也不是一無用處,它很適合用在IO密集型任務(wù)中。I/O密集型執(zhí)行期間大部分是時(shí)間都用在I/O上,如數(shù)據(jù)庫(kù)I/O,較少時(shí)間用在CPU計(jì)算上。因此該應(yīng)用場(chǎng)景可以使用Python多線程,當(dāng)一個(gè)任務(wù)阻塞在IO操作上時(shí),我們可以立即切換執(zhí)行其他線程上執(zhí)行其他IO操作請(qǐng)求。
總結(jié):Python多線程在IO密集型任務(wù)中還是很有用處的,而對(duì)于計(jì)算密集型任務(wù),應(yīng)該使用Python多進(jìn)程。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- python多線程threading.Lock鎖用法實(shí)例
- Python中用Ctrl+C終止多線程程序的問題解決
- python多線程編程中的join函數(shù)使用心得
- 理解python多線程(python多線程簡(jiǎn)明教程)
- 詳解Python中的多線程編程
- 淺析Python中的多進(jìn)程與多線程的使用
- Python中多線程thread與threading的實(shí)現(xiàn)方法
- Python多線程同步Lock、RLock、Semaphore、Event實(shí)例
- 基python實(shí)現(xiàn)多線程網(wǎng)頁(yè)爬蟲
- Python threading多線程編程實(shí)例
相關(guān)文章
pandas取dataframe特定行列的實(shí)現(xiàn)方法
大家在使用Python進(jìn)行數(shù)據(jù)分析時(shí),經(jīng)常要使用到的一個(gè)數(shù)據(jù)結(jié)構(gòu)就是pandas的DataFrame,本文介紹了pandas取dataframe特定行列的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05Python中私有屬性“_“下劃線和“__“雙下劃線區(qū)別
本文主要介紹了Python中私有屬性“_“下劃線和“__“雙下劃線區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03Python操作Excel數(shù)據(jù)的封裝函數(shù)分享
對(duì)比其它編程語言,我們都知道Python最大的優(yōu)勢(shì)是代碼簡(jiǎn)單,有豐富的第三方開源庫(kù)供開發(fā)者使用。而對(duì)于數(shù)據(jù)的讀取和存儲(chǔ),對(duì)于普通人來講,除了數(shù)據(jù)庫(kù)之外,最常見的就是微軟的Excel。本文為大家準(zhǔn)備了Python操作Excel數(shù)據(jù)的封裝函數(shù),希望對(duì)大家有所幫助2022-11-11Python實(shí)現(xiàn)http接口自動(dòng)化測(cè)試的示例代碼
這篇文章主要介紹了Python實(shí)現(xiàn)http接口自動(dòng)化測(cè)試的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10熵值法原理及Python實(shí)現(xiàn)的示例詳解
熵值法也稱熵權(quán)法,是學(xué)術(shù)研究及實(shí)際應(yīng)用中的一種常用且有效的編制指標(biāo)的方法。本文就來和大家聊聊熵值法原理及Python實(shí)現(xiàn),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-02-02Python做個(gè)自定義動(dòng)態(tài)壁紙還可以放視頻
這篇文章主要介紹了如何用Python做個(gè)可以放視頻自定義動(dòng)態(tài)壁紙,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08python實(shí)現(xiàn)在內(nèi)存中讀寫str和二進(jìn)制數(shù)據(jù)代碼
這篇文章主要介紹了python實(shí)現(xiàn)在內(nèi)存中讀寫str和二進(jìn)制數(shù)據(jù)代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-04-04Python分割單詞和轉(zhuǎn)換命名法的實(shí)現(xiàn)
本文主要介紹了Python分割單詞和轉(zhuǎn)換命名法的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03