先來看下面這段代碼的執(zhí)行:
如圖中的代碼所示,Python 在多線程環(huán)境下 print
的時候决帖,由于輸出顯示的資源只有一個厕九,當過多個線程想要在同一時間打印輸出的時候,有的線程會因為沒有搶到輸出顯示的資源而無法輸出地回。
在實際的執(zhí)行過程中 "Hello" 的輸出次數(shù)基本都是正常的 4 次扁远,極少的情況下出現(xiàn)如圖中所示的 3 次。而 "the arg is" 的語句輸出的次數(shù)有很大的概率不是 4 次落君。因為 i 是線程共享的變量穿香,也就是說只有一個內(nèi)存空間,而"Hello"字符串每個線程在執(zhí)行 action 方法時會為它單獨開辟一個內(nèi)存空間绎速。
還有一種情況是 print
的時候存在緩沖皮获,但是,首先在 IDE 環(huán)境下默認是沒有開啟緩沖的纹冤,而且在 print
語句后調(diào)用 sys.stdout.flush()
洒宝,強制立刻刷新緩沖依然沒有解決異常购公。
期間我還以為是因為主線程退出了,導致子線程沒執(zhí)行到 print
程序就結(jié)束運行了雁歌。但是在 Python 中宏浩,當主線程退出之后,即使之線程沒有 join
, 子線程仍然會繼續(xù)執(zhí)行靠瞎。如果希望主線程退出后比庄,其子線程也退出而不再執(zhí)行,則需要設(shè)置子線程為后臺線程乏盐。Python 提供 setDaemon
方法佳窑,將子線程與主線程進行綁定,當主線程退出時父能,子線程的生命也隨之結(jié)束神凑。
值得注意的是,雖然 print
的次數(shù)會有異常(這里的異常是指
輸出到控制臺異常何吝,實際上 print
語句是執(zhí)行了的溉委,只不過控制臺上沒有顯示)。但是將 arg
變量寫到文件里是正常的爱榕,雖然順序是混亂的瓣喊。
結(jié)論 Python print
不是線程安全的,在并發(fā)的情況下不按照正確的方式去寫會出現(xiàn)無法預期的異常呆细,正確的寫法是在 t.start()
之后加一句 t.join()
來阻塞主線程型宝,直到當前子線程執(zhí)行完畢,或者加鎖處理絮爷。
import threading
import time
def action(arg):
time.sleep(1)
sys.stdout.flush()
print 'the arg is:%s\r' %arg
print 'Hello'
for i in xrange(4):
t = threading.Thread(target=action, args=(i,))
t.start()
t.join()
import threading
import time
def action(arg):
lock.acquire()
time.sleep(1)
sys.stdout.flush()
print 'the arg is:%s\r' %arg
print 'Hello'
lock.release()
lock = threading.Lock()
for i in xrange(4):
t = threading.Thread(target=action, args=(i,))
t.start()