來(lái)源:數(shù)據(jù)分析網(wǎng)
Threading 模塊從 Python 1.5.2
版開(kāi)始出現(xiàn)耘斩,用于增強(qiáng)底層的多線(xiàn)程模塊 thread 合陵。Threading 模塊讓操作多線(xiàn)程變得更簡(jiǎn)單枢赔,并且支持程序同時(shí)運(yùn)行多個(gè)操作。
注意拥知,Python 中的多線(xiàn)程最好用于處理有關(guān) I/O 的操作踏拜,如從網(wǎng)上下載資源或者從本地讀取文件或者目錄。如果你要做的是 CPU 密集型操作低剔,那么你需要使用 Python 的 multiprocessing 模塊速梗。這樣做的原因是,Python 有一個(gè)全局解釋器鎖 (GIL)户侥,使得所有子線(xiàn)程都必須運(yùn)行在同一個(gè)主線(xiàn)程中镀琉。正因?yàn)槿绱耍?dāng)你通過(guò)多線(xiàn)程來(lái)處理多個(gè) CPU 密集型任務(wù)時(shí)蕊唐,你會(huì)發(fā)現(xiàn)它實(shí)際上運(yùn)行的更慢屋摔。因此,我們將重點(diǎn)放在那些多線(xiàn)程最擅長(zhǎng)的領(lǐng)域:I/O 操作替梨!
線(xiàn)程簡(jiǎn)介
多線(xiàn)程能讓你像運(yùn)行一個(gè)獨(dú)立的程序一樣運(yùn)行一段長(zhǎng)代碼钓试。這有點(diǎn)像調(diào)用子進(jìn)程(subprocess)装黑,不過(guò)區(qū)別是你調(diào)用的是一個(gè)函數(shù)或者一個(gè)類(lèi),而不是獨(dú)立的程序弓熏。在我看來(lái)恋谭,舉例說(shuō)明更有助于解釋。下面來(lái)看一個(gè)簡(jiǎn)單的例子:
import threading
def doubler(number):
"""
可以被線(xiàn)程使用的一個(gè)函數(shù)
"""
print(threading.currentThread().getName() + 'n')
print(number * 2)
print()
if __name__ == '__main__':
for i in range(5):
my_thread = threading.Thread(target=doubler, args=(i,))
my_thread.start()
這里挽鞠,我們導(dǎo)入 threading 模塊并且創(chuàng)建一個(gè)叫 doubler 的常規(guī)函數(shù)疚颊。這個(gè)函數(shù)接受一個(gè)值,然后把這個(gè)值翻一番信认。它還會(huì)打印出調(diào)用這個(gè)函數(shù)的線(xiàn)程的名稱(chēng)材义,并在最后打印一行空行。然后在代碼的最后一塊嫁赏,我們創(chuàng)建五個(gè)線(xiàn)程并且依次啟動(dòng)它們其掂。在我們實(shí)例化一個(gè)線(xiàn)程時(shí),你會(huì)注意到潦蝇,我們把 doubler 函數(shù)傳給 target 參數(shù)款熬,同時(shí)也給 doubler 函數(shù)傳遞了參數(shù)。Args 參數(shù)看起來(lái)有些奇怪攘乒,那是因?yàn)槲覀冃枰獋鬟f一個(gè)序列給 doubler 函數(shù)贤牛,但它只接受一個(gè)變量,所以我們把逗號(hào)放在尾部來(lái)創(chuàng)建只有一個(gè)參數(shù)的序列持灰。
需要注意的是盔夜,如果你想等待一個(gè)線(xiàn)程結(jié)束负饲,那么需要調(diào)用 join() 方法堤魁。
當(dāng)你運(yùn)行以上這段代碼,會(huì)得到以下輸出內(nèi)容:
Thread-1
0
Thread-2
2
Thread-3
4
Thread-4
6
Thread-5
8
當(dāng)然返十,通常情況下你不會(huì)希望輸出打印到標(biāo)準(zhǔn)輸出妥泉。如果不幸真的這么做了,那么最終的顯示效果將會(huì)非扯纯樱混亂盲链。你應(yīng)該使用 Python 的 logging 模塊。它是線(xiàn)程安全的迟杂,并且表現(xiàn)出色刽沾。讓我們用logging 模塊修改上面的例子并且給我們的線(xiàn)程命名。代碼如下:
import logging
import threading
def get_logger():
logger = logging.getLogger("threading_example")
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler("threading.log")
fmt = '%(asctime)s - %(threadName)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(fmt)
fh.setFormatter(formatter)
logger.addHandler(fh)
return logger
def doubler(number, logger):
"""
可以被線(xiàn)程使用的一個(gè)函數(shù)
"""
logger.debug('doubler function executing')
result = number * 2
logger.debug('doubler function ended with: {}'.format(
result))
if __name__ == '__main__':
logger = get_logger()
thread_names = ['Mike', 'George', 'Wanda', 'Dingbat', 'Nina']
for i in range(5):
my_thread = threading.Thread(
target=doubler, name=thread_names[i], args=(i,logger))
my_thread.start()
代碼中最大的改變就是加入了 get_logger 函數(shù)排拷。這段代碼將創(chuàng)建一個(gè)被設(shè)置為調(diào)試級(jí)別的日志記錄器侧漓。它將日志保存在當(dāng)前目錄(即腳本運(yùn)行所在的目錄)下,然后設(shè)置每行日志的格式监氢。格式包括時(shí)間戳布蔗、線(xiàn)程名藤违、日志記錄級(jí)別以及日志信息。
在 doubler 函數(shù)中纵揍,我們把 print 語(yǔ)句換成 logging 語(yǔ)句顿乒。你會(huì)注發(fā)現(xiàn),在創(chuàng)建線(xiàn)程時(shí)泽谨,我們給 doubler 函數(shù)傳入了 logger 對(duì)象璧榄。這樣做的原因是,如果在每個(gè)線(xiàn)程中實(shí)例化 logging 對(duì)象吧雹,那么將會(huì)產(chǎn)生多個(gè) logging 單例(singleton)犹菱,并且日志中將會(huì)有很多重復(fù)的內(nèi)容。
最后吮炕,創(chuàng)建一個(gè)名稱(chēng)列表腊脱,然后使用 name 關(guān)鍵字參數(shù)為每一個(gè)線(xiàn)程設(shè)置具體名稱(chēng),這樣就可以為線(xiàn)程命名龙亲。運(yùn)行以上代碼陕凹,將會(huì)得到包含以下內(nèi)容的日志文件:
2016-07-24 20:39:50,055 - Mike - DEBUG - doubler function executing
2016-07-24 20:39:50,055 - Mike - DEBUG - doubler function ended with: 0
2016-07-24 20:39:50,055 - George - DEBUG - doubler function executing
2016-07-24 20:39:50,056 - George - DEBUG - doubler function ended with: 2
2016-07-24 20:39:50,056 - Wanda - DEBUG - doubler function executing
2016-07-24 20:39:50,056 - Wanda - DEBUG - doubler function ended with: 4
2016-07-24 20:39:50,056 - Dingbat - DEBUG - doubler function executing
2016-07-24 20:39:50,057 - Dingbat - DEBUG - doubler function ended with: 6
2016-07-24 20:39:50,057 - Nina - DEBUG - doubler function executing
2016-07-24 20:39:50,057 - Nina - DEBUG - doubler function ended with: 8
輸出結(jié)果不言自明,所以繼續(xù)介紹其他內(nèi)容鳄炉。在本節(jié)中再多說(shuō)一點(diǎn)杜耙,即通過(guò)繼承threading.Thread 實(shí)現(xiàn)多線(xiàn)程。舉最后一個(gè)例子拂盯,通過(guò)繼承 threading.Thread 創(chuàng)建子類(lèi)佑女,而不是直接調(diào)用 Thread 函數(shù)。
更新后的代碼如下:
import logging
import threading
class MyThread(threading.Thread):
def __init__(self, number, logger):
threading.Thread.__init__(self)
self.number = number
self.logger = logger
def run(self):
"""
運(yùn)行線(xiàn)程
"""
logger.debug('Calling doubler')
doubler(self.number, self.logger)
def get_logger():
logger = logging.getLogger("threading_example")
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler("threading_class.log")
fmt = '%(asctime)s - %(threadName)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(fmt)
fh.setFormatter(formatter)
logger.addHandler(fh)
return logger
def doubler(number, logger):
"""
可以被線(xiàn)程使用的一個(gè)函數(shù)
"""
logger.debug('doubler function executing')
result = number * 2
logger.debug('doubler function ended with: {}'.format(
result))
if __name__ == '__main__':
logger = get_logger()
thread_names = ['Mike', 'George', 'Wanda', 'Dingbat', 'Nina']
for i in range(5):
thread = MyThread(i, logger)
thread.setName(thread_names[i])
thread.start()
這個(gè)例子中谈竿,我們只是創(chuàng)建一個(gè)繼承于 threading.Thread 的子類(lèi)团驱。像之前一樣,傳入一個(gè)需要翻一番的數(shù)字空凸,以及 logging 對(duì)象嚎花。但是這次,設(shè)置線(xiàn)程名稱(chēng)的方式有點(diǎn)不太一樣呀洲,變成了通過(guò)調(diào)用 thread 對(duì)象的 setName 方法來(lái)設(shè)置紊选。不過(guò)仍然需要調(diào)用 start 來(lái)啟動(dòng)線(xiàn)程,不過(guò)你可能注意到我們并不需要在子類(lèi)中定義該方法道逗。當(dāng)調(diào)用 start 時(shí)兵罢,它會(huì)通過(guò)調(diào)用 run 方法來(lái)啟動(dòng)線(xiàn)程。在我們的類(lèi)中滓窍,我們調(diào)用 doubler 函數(shù)來(lái)做處理卖词。輸出結(jié)果中除了一些添加的額外信息內(nèi)容幾乎差不多。運(yùn)行下這個(gè)腳本贰您,看看你會(huì)得到什么坏平。
線(xiàn)程鎖與線(xiàn)程同步
當(dāng)你有多個(gè)線(xiàn)程拢操,就需要考慮怎樣避免線(xiàn)程沖突。我的意思是說(shuō)舶替,你可能遇到多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)同一資源的情況令境。如果不考慮這些問(wèn)題并且制定相應(yīng)的解決方案,那么在開(kāi)發(fā)產(chǎn)品過(guò)程中顾瞪,你總會(huì)在最糟糕的時(shí)候遇到這些棘手的問(wèn)題舔庶。
解決辦法就是使用線(xiàn)程鎖。鎖由 Python 的 threading 模塊提供陈醒,并且它最多被一個(gè)線(xiàn)程所持有惕橙。當(dāng)一個(gè)線(xiàn)程試圖獲取一個(gè)已經(jīng)鎖在資源上的鎖時(shí),該線(xiàn)程通常會(huì)暫停運(yùn)行钉跷,直到這個(gè)鎖被釋放弥鹦。來(lái)讓我們看一個(gè)非常典型沒(méi)有卻應(yīng)具備鎖功能的例子:
import threading
total = 0
def update_total(amount):
"""
Updates the total by the given amount
"""
global total
total += amount
print (total)
if __name__ == '__main__':
for i in range(10):
my_thread = threading.Thread(
target=update_total, args=(5,))
my_thread.start()
如果往以上代碼添加 time.sleep
函數(shù)并給出不同長(zhǎng)度的時(shí)間,可能會(huì)讓這個(gè)例子更有意思爷辙。無(wú)論如何彬坏,這里的問(wèn)題是,一個(gè)線(xiàn)程可能已經(jīng)調(diào)用 update_total
函數(shù)并且還沒(méi)有更新完成膝晾,此時(shí)另一個(gè)線(xiàn)程也有可能調(diào)用它并且嘗試更新內(nèi)容栓始。根據(jù)操作執(zhí)行順序的不同,該值可能只被增加一次血当。
讓我們給這個(gè)函數(shù)添加鎖幻赚。有兩種方法可以實(shí)現(xiàn)。第一種方式是使用 try/finally
臊旭,從而確保鎖肯定會(huì)被釋放落恼。下面是示例:
import threading
total = 0
lock = threading.Lock()
def update_total(amount):
"""
Updates the total by the given amount
"""
global total
lock.acquire()
try:
total += amount
finally:
lock.release()
print (total)
if __name__ == '__main__':
for i in range(10):
my_thread = threading.Thread(
target=update_total, args=(5,))
my_thread.start()
如上,在我們做任何處理之前就獲取鎖巍扛。然后嘗試更新 total 的值领跛,最后釋放鎖并打印出 total 的當(dāng)前值。事實(shí)上撤奸,我們可以使用 Python 的 with 語(yǔ)句避免使用 try/finally
這種較為繁瑣的語(yǔ)句:
importt threading
total = 0
lock = threading.Lock()
def update_total(amount):
"""
Updates the total by the given amount
"""
global total
with lock:
total += amount
print (total)
if __name__ == '__main__':
for i in range(10):
my_thread = threading.Thread(
target=update_total, args=(5,))
my_thread.start()
正如你看到的那樣,我們不再需要 try/finally
作為上下文管理器喊括,而是由 with 語(yǔ)句作為替代胧瓜。
當(dāng)然你也會(huì)遇到要在代碼中通過(guò)多個(gè)線(xiàn)程訪(fǎng)問(wèn)多個(gè)函數(shù)的情況。當(dāng)你第一次編寫(xiě)并發(fā)代碼時(shí)郑什,代碼可能是這樣的:
import threading
total = 0
lock = threading.Lock()
def do_something():
lock.acquire()
try:
print('Lock acquired in the do_something function')
finally:
lock.release()
print('Lock released in the do_something function')
return "Done doing something"
def do_something_else():
lock.acquire()
try:
print('Lock acquired in the do_something_else function')
finally:
lock.release()
print('Lock released in the do_something_else function')
return "Finished something else"
if __name__ == '__main__':
result_one = do_something()
result_two = do_something_else()
這樣的代碼在上面的情況下能夠正常工作府喳,但假設(shè)你有多個(gè)線(xiàn)程都調(diào)用這兩個(gè)函數(shù)呢。當(dāng)一個(gè)線(xiàn)程正在運(yùn)行這兩個(gè)函數(shù)蘑拯,然后另外一個(gè)線(xiàn)程也可能會(huì)修改這些數(shù)據(jù)钝满,最后得到的就是不正確的結(jié)果兜粘。問(wèn)題是,你甚至可能沒(méi)有馬上意識(shí)到結(jié)果錯(cuò)了弯蚜。有什么解決辦法呢孔轴?讓我們?cè)囍页龃鸢浮?/p>
通常首先想到的就是在調(diào)用這兩個(gè)函數(shù)的地方上鎖。讓我們?cè)囍薷纳厦娴睦铀檗啵薷某扇缦滤荆?/p>
import threading
total = 0
lock = threading.RLock()
def do_something():
with lock:
print('Lock acquired in the do_something function')
print('Lock released in the do_something function')
return "Done doing something"
def do_something_else():
with lock:
print('Lock acquired in the do_something_else function')
print('Lock released in the do_something_else function')
return "Finished something else"
def main():
with lock:
result_one = do_something()
result_two = do_something_else()
print (result_one)
print (result_two)
if __name__ == '__main__':
main()
當(dāng)你真正運(yùn)行這段代碼時(shí)路鹰,你會(huì)發(fā)現(xiàn)它只是掛起了。究其原因收厨,是因?yàn)槲覀冎桓嬖Vthreading
模塊獲取鎖晋柱。所以當(dāng)我們調(diào)用第一個(gè)函數(shù)時(shí),它發(fā)現(xiàn)鎖已經(jīng)被獲取诵叁,隨后便把自己掛起了雁竞,直到鎖被釋放,然而這將永遠(yuǎn)不會(huì)發(fā)生拧额。
真正的解決辦法是使用重入鎖(Re-Entrant Lock
)浓领。threading 模塊提供的解決辦法是使用RLock 函數(shù)。即把lock = threading.lock()
替換為lock = threading.RLock()
势腮,然后重新運(yùn)行代碼联贩,現(xiàn)在代碼就可以正常運(yùn)行了。
如果你想在線(xiàn)程中運(yùn)行以上代碼捎拯,那么你可以用以下代碼取代直接調(diào)用 main 函數(shù):
if __name__ == '__main__':
for i in range(10):
my_thread = threading.Thread(
target=main)
my_thread.start()
每個(gè)線(xiàn)程都會(huì)運(yùn)行 main 函數(shù)泪幌,main 函數(shù)則會(huì)依次調(diào)用另外兩個(gè)函數(shù)。最終也會(huì)產(chǎn)生 10 組結(jié)果集署照。
定時(shí)器
Threading 模塊有一個(gè)優(yōu)雅的 Timer 類(lèi)祸泪,你可以用它來(lái)實(shí)現(xiàn)在指定時(shí)間后要發(fā)生的動(dòng)作。它們實(shí)際上會(huì)啟動(dòng)自己的自定義線(xiàn)程建芙,通過(guò)調(diào)用常規(guī)線(xiàn)程上的 start() 方法即可運(yùn)行没隘。你也可以調(diào)用它的cancel 方法停止定時(shí)器。值得注意的是禁荸,你甚至可以在開(kāi)始定時(shí)器之前取消它右蒲。
有一天,我遇到一個(gè)特殊的情況:我需要與已經(jīng)啟動(dòng)的子進(jìn)程通信赶熟,但是我需要它有超時(shí)處理瑰妄。雖然處理這種特殊問(wèn)題有很多不同的方法,不過(guò)我最喜歡的解決方案是使用 threading 模塊的 Timer 類(lèi)映砖。
在下面這個(gè)例子中间坐,我們將使用 ping 指令作為演示。在 Linux 系統(tǒng)中,ping 命令會(huì)一直運(yùn)行下去直到你手動(dòng)殺死它竹宋。所以在 Linux 世界里劳澄,Timer 類(lèi)就顯得非常方便。示例如下:
import subprocess
from threading import Timer
kill = lambda process: process.kill()
cmd = ['ping', 'www.google.com']
ping = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
my_timer = Timer(5, kill, [ping])
try:
my_timer.start()
stdout, stderr = ping.communicate()
finally:
my_timer.cancel()
print (str(stdout))
這里我們?cè)?lambda 表達(dá)式中調(diào)用 kill 殺死進(jìn)程蜈七。接下來(lái)啟動(dòng) ping 命令秒拔,然后創(chuàng)建 Timer 對(duì)象。你會(huì)注意到宪潮,第一個(gè)參數(shù)就是需要等待的秒數(shù)溯警,第二個(gè)參數(shù)是需要調(diào)用的函數(shù),緊跟其后的參數(shù)是要調(diào)用函數(shù)的入?yún)⒔葡唷T诒纠刑萸幔覀兊暮瘮?shù)是一個(gè) lambda 表達(dá)式,傳入的是一個(gè)只有一個(gè)元素的列表尽棕。如果你運(yùn)行這段代碼喳挑,它應(yīng)該會(huì)運(yùn)行 5 秒鐘,然后打印出 ping 的結(jié)果滔悉。
其他線(xiàn)程組件
Threading 模塊包含對(duì)其他功能的支持伊诵。例如,你可以創(chuàng)建信號(hào)量(Semaphore)回官,這是計(jì)算機(jī)科學(xué)中最古老的同步原語(yǔ)之一曹宴。基本上歉提,一個(gè)信號(hào)量管理一個(gè)內(nèi)置的計(jì)數(shù)器笛坦。當(dāng)你調(diào)用 acquire 時(shí)計(jì)數(shù)器就會(huì)遞減,相反當(dāng)你調(diào)用 release 時(shí)就會(huì)遞增苔巨。根據(jù)其設(shè)計(jì)版扩,計(jì)數(shù)器的值無(wú)法小于零,所以如果正好在計(jì)數(shù)器為零時(shí)調(diào)用 acquire 方法侄泽,該方法將阻塞線(xiàn)程礁芦。
譯者注:通常使用信號(hào)量時(shí)都會(huì)初始化一個(gè)大于零的值,如
semaphore = threading.Semaphore(2)
另一個(gè)非常有用的同步工具就是事件(Event)悼尾。它允許你使用信號(hào)(signal)實(shí)現(xiàn)線(xiàn)程通信柿扣。在下一節(jié)中我們將舉一個(gè)使用事件的實(shí)例。
最后诀豁,在 Python 3.2
中加入了 Barrier 對(duì)象窄刘。Barrier 是管理線(xiàn)程池中的同步原語(yǔ),在線(xiàn)程池中多條線(xiàn)程需要相互等待對(duì)方舷胜。如果要傳遞 barrier,每一條線(xiàn)程都要調(diào)用 wait() 方法,在其他線(xiàn)程調(diào)用該方法之前線(xiàn)程將會(huì)阻塞烹骨。全部調(diào)用之后將會(huì)同時(shí)釋放所有線(xiàn)程翻伺。
線(xiàn)程通信
某些情況下,你會(huì)希望線(xiàn)程之間互相通信沮焕。就像先前提到的吨岭,你可以通過(guò)創(chuàng)建 Event 對(duì)象達(dá)到這個(gè)目的。但更常用的方法是使用隊(duì)列(Queue)峦树。在我們的例子中辣辫,這兩種方式都會(huì)有所涉及。下面讓我們看看到底是什么樣子的:
import threading
from queue import Queue
def creator(data, q):
"""
生成用于消費(fèi)的數(shù)據(jù)魁巩,等待消費(fèi)者完成處理
"""
print('Creating data and putting it on the queue')
for item in data:
evt = threading.Event()
q.put((item, evt))
print('Waiting for data to be doubled')
evt.wait()
def my_consumer(q):
"""
消費(fèi)部分?jǐn)?shù)據(jù)急灭,并做處理
這里所做的只是將輸入翻一倍
"""
while True:
data, evt = q.get()
print('data found to be processed: {}'.format(data))
processed = data * 2
print(processed)
evt.set()
q.task_done()
if __name__ == '__main__':
q = Queue()
data = [5, 10, 13, -1]
thread_one = threading.Thread(target=creator, args=(data, q))
thread_two = threading.Thread(target=my_consumer, args=(q,))
thread_one.start()
thread_two.start()
q.join()
讓我們掰開(kāi)揉碎分析一下。首先谷遂,我們有一個(gè)創(chuàng)建者(creator)函數(shù)(亦稱(chēng)作生產(chǎn)者(producer))葬馋,我們用它來(lái)創(chuàng)建想要操作(或者消費(fèi))的數(shù)據(jù)。然后用另外一個(gè)函數(shù)my_consumer 來(lái)處理剛才創(chuàng)建出來(lái)的數(shù)據(jù)肾扰。Creator 函數(shù)使用 Queue 的 put 方法向隊(duì)列中插入數(shù)據(jù)畴嘶,消費(fèi)者將會(huì)持續(xù)不斷的檢測(cè)有沒(méi)有更多的數(shù)據(jù),當(dāng)發(fā)現(xiàn)有數(shù)據(jù)時(shí)就會(huì)處理數(shù)據(jù)集晚。Queue 對(duì)象處理所有的獲取鎖和釋放鎖的過(guò)程窗悯,這些不用我們太關(guān)心。
在這個(gè)例子中偷拔,先創(chuàng)建一個(gè)列表蒋院,然后創(chuàng)建兩個(gè)線(xiàn)程,一個(gè)用作生產(chǎn)者条摸,一個(gè)作為消費(fèi)者悦污。你會(huì)發(fā)現(xiàn),我們給兩個(gè)線(xiàn)程都傳遞了 Queue 對(duì)象钉蒲,這兩個(gè)線(xiàn)程隱藏了關(guān)于鎖處理的細(xì)節(jié)切端。隊(duì)列實(shí)現(xiàn)了數(shù)據(jù)從第一個(gè)線(xiàn)程到第二個(gè)線(xiàn)程的傳遞。當(dāng)?shù)谝粋€(gè)線(xiàn)程把數(shù)據(jù)放入隊(duì)列時(shí)顷啼,同時(shí)也傳遞一個(gè) Event 事件踏枣,緊接著掛起自己,等待該事件結(jié)束钙蒙。在消費(fèi)者側(cè)茵瀑,也就是第二個(gè)線(xiàn)程,則做數(shù)據(jù)處理工作躬厌。當(dāng)完成數(shù)據(jù)處理后就會(huì)調(diào)用 Event 事件的 set 方法马昨,通知第一個(gè)線(xiàn)程已經(jīng)把數(shù)據(jù)處理完畢了,可以繼續(xù)生產(chǎn)了。
最后一行代碼調(diào)用了 Queue 對(duì)象的 join 方法鸿捧,它會(huì)告知 Queue 等待所有線(xiàn)程結(jié)束屹篓。當(dāng)?shù)谝粋€(gè)線(xiàn)程把所有數(shù)據(jù)都放到隊(duì)列中,它也就運(yùn)行結(jié)束了匙奴。
結(jié)束語(yǔ)
以上涵蓋了關(guān)于線(xiàn)程的諸多方面堆巧,主要包括:
線(xiàn)程基礎(chǔ)知識(shí)
鎖的工作方式
什么是事件以及如何使用
如何使用定時(shí)器
通過(guò) Queues/Events 實(shí)現(xiàn)線(xiàn)程間通信
現(xiàn)在你們知道如何使用線(xiàn)程以及線(xiàn)程擅長(zhǎng)什么了,希望在你們的代碼中能有它們的用武之地泼菌。
本文作者為 Michael Driscool谍肤,是其新書(shū) Python 201 的一節(jié)。本文譯者為 linkcheng哗伯,譯者簡(jiǎn)介:linkcheng荒揣,專(zhuān)業(yè)電子信息工程。已有兩年工作經(jīng)驗(yàn)笋颤,從事 c/c++ 開(kāi)發(fā)乳附。目前在學(xué)習(xí) flask,希望以后自己可以搭建網(wǎng)站伴澄。
今年第六屆大會(huì)PyConChina2016赋除,由PyChina.org發(fā)起,CPyUG/TopGeek 等社區(qū)協(xié)辦非凌,將在2016年9月10日(上海)9月23日(深圳)10月15日(北京)地舉辦的針對(duì)Python開(kāi)發(fā)者所舉辦的最盛大和權(quán)威的Python相關(guān)技術(shù)會(huì)議举农,由PyChina社區(qū)主辦,致力于推動(dòng)各類(lèi)Python相關(guān)的技術(shù)在互聯(lián)網(wǎng)敞嗡、企業(yè)應(yīng)用等領(lǐng)域的研發(fā)和應(yīng)用颁糟。
您可以點(diǎn)擊此處
了解更多詳情,或者掃描下圖二維碼: