日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Py之MT:Multithreaded的简介、引入、使用方法之详细攻略

發(fā)布時(shí)間:2025/3/21 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Py之MT:Multithreaded的简介、引入、使用方法之详细攻略 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Py之MT:Multithreaded的簡(jiǎn)介、引入、使用方法之詳細(xì)攻略

?

?

?

目錄

Multithreaded的簡(jiǎn)介

Multithreaded的引入

Multithreaded的使用方法

1、Python提供了幾個(gè)用于多線程編程的模塊

2、Python中使用線程有兩種方式:函數(shù)或者用類來(lái)包裝線程對(duì)象。

案例應(yīng)用

(1)、T1、創(chuàng)建Thread實(shí)例,傳給它一個(gè)函數(shù)

(2)、T2、創(chuàng)建一個(gè)Thread實(shí)例,傳給它一個(gè)可調(diào)用的類對(duì)象。


?

?

?

Multithreaded的簡(jiǎn)介

? ? ? ? 多線程(英語(yǔ):multithreading),是指從軟件或者硬件上實(shí)現(xiàn)多個(gè)線程并發(fā)執(zhí)行的技術(shù)。具有多線程能力的計(jì)算機(jī)因有硬件支持而能夠在同一時(shí)間執(zhí)行多于一個(gè)線程,進(jìn)而提升整體處理性能。具有這種能力的系統(tǒng)包括對(duì)稱多處理機(jī)、多核心處理器以及芯片級(jí)多處理(Chip-level multithreading)或同時(shí)多線程(Simultaneous multithreading)處理器。 [1] ?在一個(gè)程序中,這些獨(dú)立運(yùn)行的程序片段叫作“線程”(Thread),利用它編程的概念就叫作“多線程處理(Multithreading)”。具有多線程能力的計(jì)算機(jī)因有硬件支持而能夠在同一時(shí)間執(zhí)行多于一個(gè)線程(臺(tái)灣譯作“執(zhí)行緒”),進(jìn)而提升整體處理性能。
? ? ? 在計(jì)算機(jī)編程中,一個(gè)基本的概念就是同時(shí)對(duì)多個(gè)任務(wù)加以控制。許多程序設(shè)計(jì)問(wèn)題都要求程序能夠停下手頭的工作,改為處理其他一些問(wèn)題,再返回主進(jìn)程??梢酝ㄟ^(guò)多種途徑達(dá)到這個(gè)目的。

1、多線程類似于同時(shí)執(zhí)行多個(gè)不同程序,多線程運(yùn)行有如下優(yōu)點(diǎn):
(1)、使用線程可以把占據(jù)長(zhǎng)時(shí)間的程序中的任務(wù)放到后臺(tái)去處理。
用戶界面可以更加吸引人,這樣比如用戶點(diǎn)擊了一個(gè)按鈕去觸發(fā)某些事件的處理,可以彈出一個(gè)進(jìn)度條來(lái)顯示處理的進(jìn)度
(2)、程序的運(yùn)行速度可能加快
在一些等待的任務(wù)實(shí)現(xiàn)上如用戶輸入、文件讀寫和網(wǎng)絡(luò)收發(fā)數(shù)據(jù)等,線程就比較有用了。在這種情況下我們可以釋放一些珍貴的資源如內(nèi)存占用等等。
? ? ? ?線程在執(zhí)行過(guò)程中與進(jìn)程還是有區(qū)別的。每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制。
每個(gè)線程都有他自己的一組CPU寄存器,稱為線程的上下文,該上下文反映了線程上次運(yùn)行該線程的CPU寄存器的狀態(tài)。
指令指針和堆棧指針寄存器是線程上下文中兩個(gè)最重要的寄存器,線程總是在進(jìn)程得到上下文中運(yùn)行的,這些地址都用于標(biāo)志擁有線程的進(jìn)程地址空間中的內(nèi)存。
(1)、線程可以被搶占(中斷)。
(2)、在其他線程正在運(yùn)行時(shí),線程可以暫時(shí)擱置(也稱為睡眠) -- 這就是線程的退讓。
2、什么是線程?
? ? ? ? 線程(亦稱為輕量級(jí)進(jìn)程)跟進(jìn)程有些相似,不同的是:所有的線程運(yùn)行在同一個(gè)進(jìn)程中,共享相同的運(yùn)行環(huán)境。它們可以被想象成是在主進(jìn)程或“主線程”中并行運(yùn)行的“迷你進(jìn)程”。
? ? ? ? 線程有開(kāi)始,順序執(zhí)行和結(jié)束三部分。它有一個(gè)自己的指令指針,記錄自己運(yùn)行到什么地方。線程的運(yùn)行可能被搶占(中斷)或暫時(shí)的被掛起(睡眠),讓其他線程運(yùn)行,這叫做讓步。
? ? ? ? 一個(gè)進(jìn)程中的各個(gè)線程之間共享同一片數(shù)據(jù)空間,所以線程之間可以比進(jìn)程之間更方便地共享數(shù)據(jù)以及相互通訊。線程一般都是并發(fā)執(zhí)行的,正是由于這種并行和數(shù)據(jù)共享的機(jī)制使得多個(gè)任務(wù)的合作變成可能。
? ? ? ? 實(shí)際上,在單CPU的系統(tǒng)中,真正的并發(fā)是不可能的,每個(gè)線程會(huì)被安排成每次只運(yùn)行一小會(huì),然后就把CPU讓出來(lái),讓其他的線程去運(yùn)行。在進(jìn)程的整個(gè)運(yùn)行過(guò)程中,每個(gè)線程都只做自己的事,在需要的時(shí)候跟其他的線程共享運(yùn)行的結(jié)果。
? ? ? ? 當(dāng)然,這樣的共享并不是完全沒(méi)有危險(xiǎn)的。如果多個(gè)線程共同訪問(wèn)同一片數(shù)據(jù),則由于數(shù)據(jù)訪問(wèn)的順序不同,有可能導(dǎo)致數(shù)據(jù)結(jié)果的不一致的問(wèn)題,即競(jìng)態(tài)條件(race condition)。同樣,大多數(shù)線程庫(kù)都帶有一些列的同步原語(yǔ),來(lái)控制線程的執(zhí)行和數(shù)據(jù)的訪問(wèn)。
? ? ? ? 另一個(gè)需要注意的是由于有的函數(shù)會(huì)在完成之前阻塞住,在沒(méi)有特別為多線程做修改的情況下,這種“貪婪”的函數(shù)會(huì)讓CPU的時(shí)間分配有所傾斜,導(dǎo)致各個(gè)線程分配到的運(yùn)行時(shí)間可能不盡相同,不盡公平。

?

?

?

Multithreaded的引入

1.為什么引入多線程編程?
? ? ? ? 在多線程(Multithreaded,MT)編程出現(xiàn)之前,電腦程序的運(yùn)行由一個(gè)執(zhí)行序列組成,執(zhí)行序列按順序在主機(jī)的中央處理器CPU中運(yùn)行。即使整個(gè)程序由多個(gè)相互獨(dú)立無(wú)關(guān)的子任務(wù)組成,程序都會(huì)順序執(zhí)行。
? ? ? ? 由于并行處理可以大幅度地提升整個(gè)任務(wù)的效率,故引入多線程編程。
? ? ? ? 多線程中任務(wù)具有以下特點(diǎn):
? ? ? ? (1) 這些任務(wù)的本質(zhì)是異步的,需要有多個(gè)并發(fā)事務(wù);
? ? ? ? (2) 各個(gè)事務(wù)的運(yùn)行順序可以是不確定的、隨機(jī)的、不可預(yù)測(cè)的。
? ? ? ? 這樣的編程任務(wù)可以分成多個(gè)執(zhí)行流,每個(gè)流都有一個(gè)要完成的目標(biāo)。再根據(jù)不同的應(yīng)用,這些子任務(wù)可能都要計(jì)算出一個(gè)中間結(jié)果,用于合并得到最后的結(jié)果。

?

?

?

Multithreaded的使用方法

1、Python提供了幾個(gè)用于多線程編程的模塊

包括thread、threading和Queue等。 ? ? ? ?
?(1) thread模塊: 允許程序員創(chuàng)建和管理線程,它提供了基本的線程和鎖的支持。thread模塊僅僅了解就行,你應(yīng)該使用更高級(jí)別的threading等。
原因之一:thread不支持守護(hù)線程 :其中thread模塊需要避免的一個(gè)原因是:它不支持守護(hù)線程。當(dāng)主線程退出時(shí),所有的子線程不論它們是否還在工作,都會(huì)被強(qiáng)行退出。有時(shí)我們并不期望這種行為,這就引入了守護(hù)線程的概念。Threading模塊支持守護(hù)線程。 ??
原因之二:threading的Thread類是主要的運(yùn)行對(duì)象。它有很多thread模塊里沒(méi)有的函數(shù)。??

(2) threading模塊: 允許程序員創(chuàng)建和管理線程,它提供了更高級(jí)別,更強(qiáng)的線程管理的功能。

Thread #表示一個(gè)線程的執(zhí)行的對(duì)象 Lock #鎖原語(yǔ)對(duì)象(跟thread模塊里的鎖對(duì)象相同) RLock #可重入鎖對(duì)象。使單線程可以再次獲得已經(jīng)獲得了的鎖(遞歸鎖定) Condition #條件變量對(duì)象能讓一個(gè)線程停下來(lái),等待其他線程滿足了某個(gè)“條件”。如狀態(tài)的改變或值的改變 Event #通用的條件變量。多個(gè)線程可以等待某個(gè)時(shí)間的發(fā)生,在事件發(fā)生后,所有的線程都被激活 Semaphore #為等待鎖的線程提供一個(gè)類似“等候室”的結(jié)構(gòu) BoundedSemaphore #與Semaphore類似,只是它不允許超過(guò)初始值 Timer #與thread類似,只是它要等待一段時(shí)間后才開(kāi)始運(yùn)行

(3) Queue模塊: 允許用戶創(chuàng)建一個(gè)可用于多個(gè)線程間共享數(shù)據(jù)的隊(duì)列數(shù)據(jù)結(jié)構(gòu)。

2、Python中使用線程有兩種方式:函數(shù)或者用類來(lái)包裝線程對(duì)象。

函數(shù)式:調(diào)用thread模塊中的start_new_thread()函數(shù)來(lái)產(chǎn)生新線程。語(yǔ)法如下:thread.start_new_thread ( function, args[, kwargs] )
參數(shù)說(shuō)明:
function - 線程函數(shù)。
args - 傳遞給線程函數(shù)的參數(shù),他必須是個(gè)tuple類型。
kwargs - 可選參數(shù)。

(0.1)、沒(méi)有線程的舉例:使用time.sleep()函數(shù)來(lái)演示線程的工作,這個(gè)例子主要為后面線程做對(duì)比。
設(shè)計(jì)兩個(gè)計(jì)時(shí)器,loop01睡眠4秒,loop02睡眠2秒,它們是在一個(gè)進(jìn)程或者線程中,順序地執(zhí)行l(wèi)oop01()和loop02(),總運(yùn)行時(shí)間為6秒。

# time.sleep()需要一個(gè)浮點(diǎn)型的參數(shù),來(lái)指定“睡眠”的時(shí)間(單位秒)。這就相當(dāng)于程序的運(yùn)行會(huì)被掛起指定的時(shí)間。#設(shè)計(jì)兩個(gè)計(jì)時(shí)器,loop01睡眠4秒,loop02睡眠2秒,它們是在一個(gè)進(jìn)程或者線程中,順序地執(zhí)行l(wèi)oop01()和loop02(),總運(yùn)行時(shí)間為6秒。 from time import sleep, ctime def loop01(): print ("Start loop01 at:", ctime()) sleep(4) print ("Loop01 done at:", ctime()) def loop02(): print ("Start loop02 at:", ctime()) sleep(2) print ("Loop02 done at:", ctime()) def main(): print ("Starting at:", ctime()) loop01() loop02() print ("All done at:", ctime()) if __name__ == "__main__": main()


(0.2)、使用thread模塊提供簡(jiǎn)單的額多線程機(jī)制。loop01和loop02并發(fā)地被執(zhí)行,顯然,短的那個(gè)先結(jié)束??偟倪\(yùn)行時(shí)間為最慢的那個(gè)線程的運(yùn)行時(shí)間,而不是所有的線程的運(yùn)行時(shí)間之和。 # start_new_thread()要求一定要有前兩個(gè)參數(shù),即使運(yùn)行的函數(shù)不要參數(shù),也要傳一個(gè)空的元組。?
? ? ?主函數(shù)中多了個(gè)sleep(6),是因?yàn)槿绻覀儧](méi)有讓主線程停下來(lái),那主線程就會(huì)運(yùn)行下一條語(yǔ)句,顯示“All done”,然后就關(guān)閉運(yùn)行著loop01和loop02的兩個(gè)線程,退出了。沒(méi)有寫讓主線程停下來(lái)等所有子線程結(jié)束后再繼續(xù)運(yùn)行的代碼,這就是前面所說(shuō)的需要同步的原因。這里采用sleep(6)作為同步機(jī)制,設(shè)置6秒=4+2秒,在主線程等待6秒后就結(jié)束了。

from time import sleep, ctime def loop01(): print ("Start loop01 at:", ctime()) sleep(4) print ("Loop01 done at:", ctime()) def loop02(): print ("Start loop02 at:", ctime()) sleep(2) print ("Loop02 done at:", ctime()) def main(): try: print ("Starting at:", ctime()) thread.start_new_thread(loop01, ()) thread.start_new_thread(loop02, ()) sleep(6) print ("All done at:", ctime()) except Exception as e:print("Error:",e) finally: print("END\n") if __name__ == "__main__": main()


(0.3)、線程加鎖方法
? ? ? 有什么好的管理線程的方法呢?而不是在主線程里做個(gè)額外的延時(shí)6秒操作。因?yàn)榭偟倪\(yùn)行時(shí)間并不比單線程的代碼少;而且使用sleep()函數(shù)做線程的同步操作是不可靠的;如果循環(huán)的執(zhí)行時(shí)間不能事先確定的話,這可能會(huì)造成主線程過(guò)早或過(guò)晚的退出。
? ? ? ? 這就需要引入鎖的概念。下面代碼執(zhí)行l(wèi)oop函數(shù),與前面代碼的區(qū)別是不用為線程什么時(shí)候結(jié)束再做額外的等待了。使用鎖之后,可以在兩個(gè)線程都退出后,馬上退出。

from time import sleep, ctime loops = [2,4] #等待時(shí)間2、4秒。def loop(nloop, nsec, lock): #鎖序號(hào),等待時(shí)間,鎖對(duì)象 #每個(gè)線程都會(huì)被分配一個(gè)事先已經(jīng)獲得的鎖,在sleep()的時(shí)間到了之后就釋放相應(yīng)的鎖以通知住線程,這個(gè)線程已經(jīng)結(jié)束了。print("start loop", nloop, "at:", ctime()) sleep(nsec)print("loop ", nloop, "done at:", ctime()) lock.release() #解鎖。線程結(jié)束時(shí)需做解鎖操作,調(diào)用lock.release()函數(shù); def main(): print("starting at:", ctime() ) locks =[] nloops = range(len(loops)) #以loops數(shù)組創(chuàng)建列表并賦值給nloops #第一個(gè)for循環(huán)創(chuàng)建鎖:thread.allocate_lock()for i in nloops: lock = thread.allocate_lock() #創(chuàng)建鎖對(duì)象,后邊添加到locks列表內(nèi) lock.acquire() #獲取鎖對(duì)象,加鎖。即分別調(diào)用各個(gè)鎖的acquire()函數(shù)獲得鎖對(duì)象。獲得鎖表示“把鎖鎖上”,并放到鎖列表locks中;locks.append(lock) #追加到locks[]數(shù)組中 #第二個(gè)for循環(huán)創(chuàng)建線程:thread.start_new_thread(對(duì)應(yīng)線程循環(huán)號(hào),睡眠時(shí)間,鎖)for i in nloops: thread.start_new_thread(loop,(i,loops[i],locks[i])) #執(zhí)行多線程 (自定義的loop函數(shù),函數(shù)參數(shù)) #最后這個(gè)循環(huán)是一直等待(達(dá)到暫停主線程的目的):直到兩個(gè)鎖都被解鎖才繼續(xù)運(yùn)行。它是順序檢查每個(gè)鎖,主線程需不停地對(duì)所有鎖進(jìn)行檢查直到都釋放。for i in nloops: #循環(huán)等待順序檢查每個(gè)所都被解鎖才停止 while locks[i].locked(): passprint("all end:", ctime() ) if __name__ == "__main__": main()

?

案例應(yīng)用

(1)、T1、創(chuàng)建Thread實(shí)例,傳給它一個(gè)函數(shù)

?

#T1、創(chuàng)建Thread實(shí)例,傳給它一個(gè)函數(shù) import threading from time import sleep, ctime loops = [2,4] #睡眠時(shí)間 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對(duì)象nloops = range(len(loops)) #列表[0,1] #創(chuàng)建線程:在實(shí)例化每個(gè)Thread對(duì)象時(shí),把函數(shù)(target)和參數(shù)(args)都傳進(jìn)去,得到返回的Thread實(shí)例。for i in nloops: #實(shí)例化一個(gè)Thread調(diào)用Thread()方法與調(diào)用thread.start_new_thread()之間的最大區(qū)別是:#新的線程不會(huì)立即開(kāi)始。在你創(chuàng)建線程對(duì)象,但不想馬上開(kāi)始運(yùn)行線程的時(shí)候,這是一個(gè)很有用的同步特性。t = threading.Thread(target=loop,args=(i,loops[i])) threads.append(t) #開(kāi)始線程:Change1:所有的線程都創(chuàng)建之后,再一起調(diào)用start()函數(shù)啟動(dòng)線程,而不是創(chuàng)建一個(gè)啟動(dòng)一個(gè)。for i in nloops: threads[i].start() #等待所有結(jié)束線程:threading模塊的Thread類有一個(gè)join()函數(shù),允許主線程等待線程的結(jié)束。#Change2:不用再管理一堆鎖(分配鎖、獲得鎖、釋放鎖、檢查鎖的狀態(tài)等),只要簡(jiǎn)單地對(duì)每個(gè)線程調(diào)用join()函數(shù)就可以了。# 使用join()比使用一個(gè)等待鎖釋放的無(wú)限循環(huán)清楚一些(也稱“自旋鎖”)。#Tip:如果你的主線程除了等線程結(jié)束外,還有其他的事情要做(如處理或等待其他的客戶請(qǐng)求),那就不用調(diào)用join(),只有在你要等待線程結(jié)束的時(shí)候才要調(diào)用join()。for i in nloops: threads[i].join() print('All end:', ctime())if __name__ == '__main__': main()

(2)、T2、創(chuàng)建一個(gè)Thread實(shí)例,傳給它一個(gè)可調(diào)用的類對(duì)象。

? ? ?這個(gè)方法,與T1方法傳遞一個(gè)函數(shù)很相似。但它是傳一個(gè)可調(diào)用的類的實(shí)例供線程啟動(dòng)的時(shí)候執(zhí)行,這是多線程編程的一個(gè)更為面向?qū)ο蟮姆椒āO鄬?duì)于一個(gè)或幾個(gè)函數(shù)來(lái)說(shuō),由于類對(duì)象里可以使用類請(qǐng)打的功能,可以保存更多的信息,這種方法更為靈活。

#T2、創(chuàng)建一個(gè)Thread實(shí)例,傳給它一個(gè)可調(diào)用的類對(duì)象 #coding=utf-8 import threading from time import sleep, ctime loops = [2,4] #睡眠時(shí)間 #創(chuàng)建Thread對(duì)象時(shí)會(huì)實(shí)例化一個(gè)可調(diào)用類ThreadFunc的類對(duì)象。這個(gè)類保存了函數(shù)的參數(shù),函數(shù)本身以及函數(shù)的名字字符串 class ThreadFunc(object): #傳遞的是一個(gè)可調(diào)用的類,而不是一個(gè)函數(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)造器中;#由于我們有一個(gè)參數(shù)的元組,這時(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í)例化的對(duì)象,創(chuàng)建所有線程 t = threading.Thread( target=ThreadFunc(loop, (i,loops[i]), loop.__name__) ) threads.append(t) #開(kāi)始線程 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)存在于一個(gè)元組或字典中時(shí),間接地調(diào)用函數(shù)。 # args是一個(gè)包含將要提供給函數(shù)的按位置傳遞的參數(shù)的元組。如果省略了args,任何參數(shù)都不會(huì)被傳遞, # kwargs是一個(gè)包含關(guān)鍵字參數(shù)的字典。 def say(a, b): print(a, b) apply(say,("第一個(gè)參數(shù)", "Python線程")) #輸出得到:第一個(gè)參數(shù) Python線程

?

?

參考文章
Python 多線程
Python線程指南

?

?

?

?

《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的Py之MT:Multithreaded的简介、引入、使用方法之详细攻略的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。