Py之MT:Multithreaded的简介、引入、使用方法之详细攻略
Py之MT:Multithreaded的簡介、引入、使用方法之詳細(xì)攻略
?
?
?
目錄
Multithreaded的簡介
Multithreaded的引入
Multithreaded的使用方法
1、Python提供了幾個用于多線程編程的模塊
2、Python中使用線程有兩種方式:函數(shù)或者用類來包裝線程對象。
案例應(yīng)用
(1)、T1、創(chuàng)建Thread實(shí)例,傳給它一個函數(shù)
(2)、T2、創(chuàng)建一個Thread實(shí)例,傳給它一個可調(diào)用的類對象。
?
?
?
Multithreaded的簡介
? ? ? ? 多線程(英語:multithreading),是指從軟件或者硬件上實(shí)現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)。具有多線程能力的計(jì)算機(jī)因有硬件支持而能夠在同一時間執(zhí)行多于一個線程,進(jìn)而提升整體處理性能。具有這種能力的系統(tǒng)包括對稱多處理機(jī)、多核心處理器以及芯片級多處理(Chip-level multithreading)或同時多線程(Simultaneous multithreading)處理器。 [1] ?在一個程序中,這些獨(dú)立運(yùn)行的程序片段叫作“線程”(Thread),利用它編程的概念就叫作“多線程處理(Multithreading)”。具有多線程能力的計(jì)算機(jī)因有硬件支持而能夠在同一時間執(zhí)行多于一個線程(臺灣譯作“執(zhí)行緒”),進(jìn)而提升整體處理性能。
 ? ? ? 在計(jì)算機(jī)編程中,一個基本的概念就是同時對多個任務(wù)加以控制。許多程序設(shè)計(jì)問題都要求程序能夠停下手頭的工作,改為處理其他一些問題,再返回主進(jìn)程。可以通過多種途徑達(dá)到這個目的。
 1、多線程類似于同時執(zhí)行多個不同程序,多線程運(yùn)行有如下優(yōu)點(diǎn):
 (1)、使用線程可以把占據(jù)長時間的程序中的任務(wù)放到后臺去處理。
 用戶界面可以更加吸引人,這樣比如用戶點(diǎn)擊了一個按鈕去觸發(fā)某些事件的處理,可以彈出一個進(jìn)度條來顯示處理的進(jìn)度
 (2)、程序的運(yùn)行速度可能加快
 在一些等待的任務(wù)實(shí)現(xiàn)上如用戶輸入、文件讀寫和網(wǎng)絡(luò)收發(fā)數(shù)據(jù)等,線程就比較有用了。在這種情況下我們可以釋放一些珍貴的資源如內(nèi)存占用等等。
 ? ? ? ?線程在執(zhí)行過程中與進(jìn)程還是有區(qū)別的。每個獨(dú)立的線程有一個程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。
 每個線程都有他自己的一組CPU寄存器,稱為線程的上下文,該上下文反映了線程上次運(yùn)行該線程的CPU寄存器的狀態(tài)。
 指令指針和堆棧指針寄存器是線程上下文中兩個最重要的寄存器,線程總是在進(jìn)程得到上下文中運(yùn)行的,這些地址都用于標(biāo)志擁有線程的進(jìn)程地址空間中的內(nèi)存。
 (1)、線程可以被搶占(中斷)。
 (2)、在其他線程正在運(yùn)行時,線程可以暫時擱置(也稱為睡眠) -- 這就是線程的退讓。
 2、什么是線程?
 ? ? ? ? 線程(亦稱為輕量級進(jìn)程)跟進(jìn)程有些相似,不同的是:所有的線程運(yùn)行在同一個進(jìn)程中,共享相同的運(yùn)行環(huán)境。它們可以被想象成是在主進(jìn)程或“主線程”中并行運(yùn)行的“迷你進(jìn)程”。
 ? ? ? ? 線程有開始,順序執(zhí)行和結(jié)束三部分。它有一個自己的指令指針,記錄自己運(yùn)行到什么地方。線程的運(yùn)行可能被搶占(中斷)或暫時的被掛起(睡眠),讓其他線程運(yùn)行,這叫做讓步。
 ? ? ? ? 一個進(jìn)程中的各個線程之間共享同一片數(shù)據(jù)空間,所以線程之間可以比進(jìn)程之間更方便地共享數(shù)據(jù)以及相互通訊。線程一般都是并發(fā)執(zhí)行的,正是由于這種并行和數(shù)據(jù)共享的機(jī)制使得多個任務(wù)的合作變成可能。
 ? ? ? ? 實(shí)際上,在單CPU的系統(tǒng)中,真正的并發(fā)是不可能的,每個線程會被安排成每次只運(yùn)行一小會,然后就把CPU讓出來,讓其他的線程去運(yùn)行。在進(jìn)程的整個運(yùn)行過程中,每個線程都只做自己的事,在需要的時候跟其他的線程共享運(yùn)行的結(jié)果。
 ? ? ? ? 當(dāng)然,這樣的共享并不是完全沒有危險的。如果多個線程共同訪問同一片數(shù)據(jù),則由于數(shù)據(jù)訪問的順序不同,有可能導(dǎo)致數(shù)據(jù)結(jié)果的不一致的問題,即競態(tài)條件(race condition)。同樣,大多數(shù)線程庫都帶有一些列的同步原語,來控制線程的執(zhí)行和數(shù)據(jù)的訪問。
 ? ? ? ? 另一個需要注意的是由于有的函數(shù)會在完成之前阻塞住,在沒有特別為多線程做修改的情況下,這種“貪婪”的函數(shù)會讓CPU的時間分配有所傾斜,導(dǎo)致各個線程分配到的運(yùn)行時間可能不盡相同,不盡公平。
?
?
?
Multithreaded的引入
1.為什么引入多線程編程?
 ? ? ? ? 在多線程(Multithreaded,MT)編程出現(xiàn)之前,電腦程序的運(yùn)行由一個執(zhí)行序列組成,執(zhí)行序列按順序在主機(jī)的中央處理器CPU中運(yùn)行。即使整個程序由多個相互獨(dú)立無關(guān)的子任務(wù)組成,程序都會順序執(zhí)行。
 ? ? ? ? 由于并行處理可以大幅度地提升整個任務(wù)的效率,故引入多線程編程。
 ? ? ? ? 多線程中任務(wù)具有以下特點(diǎn):
 ? ? ? ? (1) 這些任務(wù)的本質(zhì)是異步的,需要有多個并發(fā)事務(wù);
 ? ? ? ? (2) 各個事務(wù)的運(yùn)行順序可以是不確定的、隨機(jī)的、不可預(yù)測的。
 ? ? ? ? 這樣的編程任務(wù)可以分成多個執(zhí)行流,每個流都有一個要完成的目標(biāo)。再根據(jù)不同的應(yīng)用,這些子任務(wù)可能都要計(jì)算出一個中間結(jié)果,用于合并得到最后的結(jié)果。
?
?
?
Multithreaded的使用方法
1、Python提供了幾個用于多線程編程的模塊
包括thread、threading和Queue等。 ? ? ? ?
 ?(1) thread模塊: 允許程序員創(chuàng)建和管理線程,它提供了基本的線程和鎖的支持。thread模塊僅僅了解就行,你應(yīng)該使用更高級別的threading等。
 原因之一:thread不支持守護(hù)線程 :其中thread模塊需要避免的一個原因是:它不支持守護(hù)線程。當(dāng)主線程退出時,所有的子線程不論它們是否還在工作,都會被強(qiáng)行退出。有時我們并不期望這種行為,這就引入了守護(hù)線程的概念。Threading模塊支持守護(hù)線程。 ??
 原因之二:threading的Thread類是主要的運(yùn)行對象。它有很多thread模塊里沒有的函數(shù)。??
(2) threading模塊: 允許程序員創(chuàng)建和管理線程,它提供了更高級別,更強(qiáng)的線程管理的功能。
Thread #表示一個線程的執(zhí)行的對象 Lock #鎖原語對象(跟thread模塊里的鎖對象相同) RLock #可重入鎖對象。使單線程可以再次獲得已經(jīng)獲得了的鎖(遞歸鎖定) Condition #條件變量對象能讓一個線程停下來,等待其他線程滿足了某個“條件”。如狀態(tài)的改變或值的改變 Event #通用的條件變量。多個線程可以等待某個時間的發(fā)生,在事件發(fā)生后,所有的線程都被激活 Semaphore #為等待鎖的線程提供一個類似“等候室”的結(jié)構(gòu) BoundedSemaphore #與Semaphore類似,只是它不允許超過初始值 Timer #與thread類似,只是它要等待一段時間后才開始運(yùn)行(3) Queue模塊: 允許用戶創(chuàng)建一個可用于多個線程間共享數(shù)據(jù)的隊(duì)列數(shù)據(jù)結(jié)構(gòu)。
2、Python中使用線程有兩種方式:函數(shù)或者用類來包裝線程對象。
函數(shù)式:調(diào)用thread模塊中的start_new_thread()函數(shù)來產(chǎn)生新線程。語法如下:thread.start_new_thread ( function, args[, kwargs] )
 參數(shù)說明:
 function - 線程函數(shù)。
 args - 傳遞給線程函數(shù)的參數(shù),他必須是個tuple類型。
 kwargs - 可選參數(shù)。
(0.1)、沒有線程的舉例:使用time.sleep()函數(shù)來演示線程的工作,這個例子主要為后面線程做對比。
 設(shè)計(jì)兩個計(jì)時器,loop01睡眠4秒,loop02睡眠2秒,它們是在一個進(jìn)程或者線程中,順序地執(zhí)行l(wèi)oop01()和loop02(),總運(yùn)行時間為6秒。
 (0.2)、使用thread模塊提供簡單的額多線程機(jī)制。loop01和loop02并發(fā)地被執(zhí)行,顯然,短的那個先結(jié)束。總的運(yùn)行時間為最慢的那個線程的運(yùn)行時間,而不是所有的線程的運(yùn)行時間之和。 # start_new_thread()要求一定要有前兩個參數(shù),即使運(yùn)行的函數(shù)不要參數(shù),也要傳一個空的元組。?
 ? ? ?主函數(shù)中多了個sleep(6),是因?yàn)槿绻覀儧]有讓主線程停下來,那主線程就會運(yùn)行下一條語句,顯示“All done”,然后就關(guān)閉運(yùn)行著loop01和loop02的兩個線程,退出了。沒有寫讓主線程停下來等所有子線程結(jié)束后再繼續(xù)運(yùn)行的代碼,這就是前面所說的需要同步的原因。這里采用sleep(6)作為同步機(jī)制,設(shè)置6秒=4+2秒,在主線程等待6秒后就結(jié)束了。
 (0.3)、線程加鎖方法
 ? ? ? 有什么好的管理線程的方法呢?而不是在主線程里做個額外的延時6秒操作。因?yàn)榭偟倪\(yùn)行時間并不比單線程的代碼少;而且使用sleep()函數(shù)做線程的同步操作是不可靠的;如果循環(huán)的執(zhí)行時間不能事先確定的話,這可能會造成主線程過早或過晚的退出。
 ? ? ? ? 這就需要引入鎖的概念。下面代碼執(zhí)行l(wèi)oop函數(shù),與前面代碼的區(qū)別是不用為線程什么時候結(jié)束再做額外的等待了。使用鎖之后,可以在兩個線程都退出后,馬上退出。
?
案例應(yīng)用
(1)、T1、創(chuàng)建Thread實(shí)例,傳給它一個函數(shù)
?
#T1、創(chuàng)建Thread實(shí)例,傳給它一個函數(shù) import threading from time import sleep, ctime loops = [2,4] #睡眠時間 def loop(nloop, nsec): print('Start loop', nloop, 'at:', ctime()) sleep(nsec) print('Loop ', nloop, 'done at:', ctime())def main(): print( 'Starting at:', ctime())threads = [] #添加了一些Thread對象nloops = range(len(loops)) #列表[0,1] #創(chuàng)建線程:在實(shí)例化每個Thread對象時,把函數(shù)(target)和參數(shù)(args)都傳進(jìn)去,得到返回的Thread實(shí)例。for i in nloops: #實(shí)例化一個Thread調(diào)用Thread()方法與調(diào)用thread.start_new_thread()之間的最大區(qū)別是:#新的線程不會立即開始。在你創(chuàng)建線程對象,但不想馬上開始運(yùn)行線程的時候,這是一個很有用的同步特性。t = threading.Thread(target=loop,args=(i,loops[i])) threads.append(t) #開始線程:Change1:所有的線程都創(chuàng)建之后,再一起調(diào)用start()函數(shù)啟動線程,而不是創(chuàng)建一個啟動一個。for i in nloops: threads[i].start() #等待所有結(jié)束線程:threading模塊的Thread類有一個join()函數(shù),允許主線程等待線程的結(jié)束。#Change2:不用再管理一堆鎖(分配鎖、獲得鎖、釋放鎖、檢查鎖的狀態(tài)等),只要簡單地對每個線程調(diào)用join()函數(shù)就可以了。# 使用join()比使用一個等待鎖釋放的無限循環(huán)清楚一些(也稱“自旋鎖”)。#Tip:如果你的主線程除了等線程結(jié)束外,還有其他的事情要做(如處理或等待其他的客戶請求),那就不用調(diào)用join(),只有在你要等待線程結(jié)束的時候才要調(diào)用join()。for i in nloops: threads[i].join() print('All end:', ctime())if __name__ == '__main__': main()(2)、T2、創(chuàng)建一個Thread實(shí)例,傳給它一個可調(diào)用的類對象。
? ? ?這個方法,與T1方法傳遞一個函數(shù)很相似。但它是傳一個可調(diào)用的類的實(shí)例供線程啟動的時候執(zhí)行,這是多線程編程的一個更為面向?qū)ο蟮姆椒āO鄬τ谝粋€或幾個函數(shù)來說,由于類對象里可以使用類請打的功能,可以保存更多的信息,這種方法更為靈活。
#T2、創(chuàng)建一個Thread實(shí)例,傳給它一個可調(diào)用的類對象 #coding=utf-8 import threading from time import sleep, ctime loops = [2,4] #睡眠時間 #創(chuàng)建Thread對象時會實(shí)例化一個可調(diào)用類ThreadFunc的類對象。這個類保存了函數(shù)的參數(shù),函數(shù)本身以及函數(shù)的名字字符串 class ThreadFunc(object): #傳遞的是一個可調(diào)用的類,而不是一個函數(shù)def __init__(self, func, args, name=''): #構(gòu)造器__init__()函數(shù):初始化賦值工作self.name=name self.func=func self.args=args def __call__(self): #特殊函數(shù)__call__():由于我們已經(jīng)有要用的參數(shù),所以就不用再傳到Thread()構(gòu)造器中;#由于我們有一個參數(shù)的元組,這時python2要在代碼中使用apply()函數(shù)即Python3中改為下邊。self.func(*self.args)def loop(nloop, nsec): print('Start loop', nloop, 'at:', ctime())sleep(nsec) print('Loop ', nloop, 'done at:', ctime())def main(): print('Starting at:', ctime())threads=[] nloops = range(len(loops)) #列表[0,1] for i in nloops: #調(diào)用ThreadFunc類實(shí)例化的對象,創(chuàng)建所有線程 t = threading.Thread( target=ThreadFunc(loop, (i,loops[i]), loop.__name__) ) threads.append(t) #開始線程 for i in nloops: threads[i].start() #等待所有結(jié)束線程 for i in nloops: threads[i].join() print('All end:', ctime())if __name__ == '__main__': main() #Python2中的apply函數(shù)的講解 #apply(func [, args [, kwargs ]]) 函數(shù):用于當(dāng)函數(shù)參數(shù)已經(jīng)存在于一個元組或字典中時,間接地調(diào)用函數(shù)。 # args是一個包含將要提供給函數(shù)的按位置傳遞的參數(shù)的元組。如果省略了args,任何參數(shù)都不會被傳遞, # kwargs是一個包含關(guān)鍵字參數(shù)的字典。 def say(a, b): print(a, b) apply(say,("第一個參數(shù)", "Python線程")) #輸出得到:第一個參數(shù) Python線程?
?
參考文章
Python 多線程
Python線程指南
?
?
?
?
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Py之MT:Multithreaded的简介、引入、使用方法之详细攻略的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 成功解决:利用编程向数据库插入一条记录,
- 下一篇: 成功解决TypeError: tuple
