上一篇:GIL
多線程編程有兩種方式,一種是通過Thread
類對線程進行實例化睬涧,另外一種是通過繼承Thread
類并重寫其run
方法。
通過Thread
類實例化進行多線程編程
下面是一個例子:
import threading
import time
def do_something(content, sec):
print('%s started' % content)
time.sleep(sec)
print('%s completed' % content)
def main():
thread1 = threading.Thread(target=do_something, args=('First task', 2))
thread2 = threading.Thread(target=do_something, args=('Second task', 4))
start_time = time.time()
thread1.start()
thread2.start()
print('Last time: %fs' % (time.time() - start_time))
main()
從這個例子中我們可以很容易地看出如果用Thread
類實例化的方式創(chuàng)建線程沛厨,并通過start()
方法開始線程宙地。
結(jié)果輸入如下:
First task started
Second task started
Last time: 0.000294s
First task completed
Second task completed
為什么這里的時間會是0s呢?原因是因為當我們創(chuàng)建了兩個線程并啟動后逆皮,此時的程序共有三個線程宅粥,thread1
和thread2
為子線程,main
函數(shù)的線程被稱為主線程电谣,可在線程內(nèi)部通過threading.current_thread()
來獲取當前線程信息秽梅,主線程會默認明名為'MainThread',可在創(chuàng)建線程時使用參數(shù)name
標記線程名剿牺。當開始了兩個子線程后企垦,由于三個線程并行,主線程也要繼續(xù)運行晒来,而執(zhí)行兩個start()
方法的時間很短钞诡,所以打印的時間是0s。并且這里的輸出結(jié)果包含了線程結(jié)束語句'... completed'湃崩,說明主線程運行結(jié)束荧降,程序并沒有退出,而是要等子線程運行結(jié)束后再退出攒读。
如何使得主線程退出后子線程自動退出呢朵诫?只需要對子線程調(diào)用setDaemon
方法,將子線程標記成守護線程薄扁,如下所示:
def main():
thread1 = threading.Thread(target=do_something, args=('First task', 2))
thread2 = threading.Thread(target=do_something, args=('Second task', 4))
thread1.setDaemon(True)
thread2.setDaemon(True)
start_time = time.time()
thread1.start()
thread2.start()
print('Last time: %fs' % (time.time() - start_time))
main()
我們再運行程序剪返,得到以下結(jié)果:
First task started
Second task started
Last time: 0.000235s
和我們預(yù)想的一致,主線程退出后邓梅,子線程也退出了脱盲,沒來得及打印'... completed'
語句。
另外一個問題是如何將主線程阻塞震放,等子線程運行完成后再繼續(xù)主線程宾毒,這樣我們就可以獲得兩個線程并行運行的時間了。只需要對子線程調(diào)用join
方法就可以將主線程阻塞,如下所示:
def main():
thread1 = threading.Thread(target=do_something, args=('First task', 2))
thread2 = threading.Thread(target=do_something, args=('Second task', 4))
start_time = time.time()
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print('Last time: %fs' % (time.time() - start_time))
main()
運行結(jié)果如下:
First task started
Second task started
First task completed
Second task completed
Last time: 4.004622s
從結(jié)果可以看出诈铛,運行時間并不是串行的6s乙各,而是兩者中的大者,原因在前一章中提到了幢竹,當線程遇到IO操作或者time.sleep
時耳峦,GIL會釋放,將執(zhí)行權(quán)留給其他線程焕毫。
通過繼承Thread
類并重寫其run
方法進行多線程編程
上面的方法適用于邏輯簡單明確的情況蹲坷,當代碼邏輯復(fù)雜時,最好使用這種方法邑飒,方便代碼維護循签。
如果你明白了上面的方法,只需要進行細微的改動即可疙咸,下面是一個例子:
import threading
class DoSomething(threading.Thread):
def __init__(self, content, sec):
super().__init__()
self.content = content
self.sec = sec
def run(self):
print('%s started' % self.content)
time.sleep(self.sec)
print('%s completed' % self.content)
def main():
thread1 = DoSomething('First task', 2)
thread2 = DoSomething('Second task', 4)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
main()
可以看出县匠,threading.Thread
類的start
方法,實際上就是運行其run
方法撒轮。