Python多線程中阻塞(join)與鎖(Lock)使用誤區(qū)解析
關(guān)于阻塞主線程
join的錯誤用法
Thread.join() 作用為阻塞主線程,即在子線程未返回的時候,主線程等待其返回然后再繼續(xù)執(zhí)行.
join不能與start在循環(huán)里連用
以下為錯誤代碼,代碼創(chuàng)建了5個線程,然后用一個循環(huán)激活線程,激活之后令其阻塞主線程.
threads = [Thread() for i in range(5)] for thread in threads: thread.start() thread.join()
執(zhí)行過程:
1. 第一次循環(huán)中,主線程通過start函數(shù)激活線程1,線程1進(jìn)行計算.
2. 由于start函數(shù)不阻塞主線程,在線程1進(jìn)行運算的同時,主線程向下執(zhí)行join函數(shù).
3. 執(zhí)行join之后,主線程被線程1阻塞,在線程1返回結(jié)果之前,主線程無法執(zhí)行下一輪循環(huán).
4. 線程1計算完成之后,解除對主線程的阻塞.
5. 主線程進(jìn)入下一輪循環(huán),激活線程2并被其阻塞…
如此往復(fù),可以看出,本來應(yīng)該并發(fā)的五個線程,在這里變成了順序隊列,效率和單線程無異.
join的正確用法
使用兩個循環(huán)分別處理start和join函數(shù).即可實現(xiàn)并發(fā).
threads = [Thread() for i in range(5)] for thread in threads: thread.start() for thread in threads: thread.join()
time.sleep代替join進(jìn)行調(diào)試
之前在一些項目里看到過這樣的代碼,使用time.sleep代替join手動阻塞主線程.
在所有子線程返回之前,主線程陷入無線循環(huán)而不能退出.
for thread in threads: thread.start() while 1: if thread_num == 0: break time.sleep(0.01)
關(guān)于線程鎖(threading.Lock)
單核CPU+PIL是否還需要鎖?
非原子操作 count = count + 1 理論上是線程不安全的.
使用3個線程同時執(zhí)行上述操作改變?nèi)肿兞縞ount的值,并查看程序執(zhí)行結(jié)果.
如果結(jié)果正確,則表示未出現(xiàn)線程沖突.
使用以下代碼測試
# -*- coding: utf-8 -*-
import threading
import time
count = 0
class Counter(threading.Thread):
def __init__(self, name):
self.thread_name = name
super(Counter, self).__init__(name=name)
def run(self):
global count
for i in xrange(100000):
count = count + 1
counters = [Counter('thread:%s' % i) for i in range(5)]
for counter in counters:
counter.start()
time.sleep(5)
print 'count=%s' % count
運行結(jié)果:
count=275552
事實上每次運行結(jié)果都不相同且不正確,這證明單核CPU+PIL仍無法保證線程安全,需要加鎖.
加鎖后的正確代碼:
# -*- coding: utf-8 -*-
import threading
import time
count = 0
lock = threading.Lock()
class Counter(threading.Thread):
def __init__(self, name):
self.thread_name = name
self.lock = threading.Lock()
super(Counter, self).__init__(name=name)
def run(self):
global count
global lock
for i in xrange(100000):
lock.acquire()
count = count + 1
lock.release()
counters = [Counter('thread:%s' % i) for i in range(5)]
for counter in counters:
counter.start()
time.sleep(5)
print 'count=%s' % count
結(jié)果:
count=500000
注意鎖的全局性
這是一個簡單的Python語法問題,但在邏輯復(fù)雜時有可能被忽略.
要保證鎖對于多個子線程來說是共用的,即不要在Thread的子類內(nèi)部創(chuàng)建鎖.
以下為錯誤代碼
# -*- coding: utf-8 -*-
import threading
import time
count = 0
# lock = threading.Lock() # 正確的聲明位置
class Counter(threading.Thread):
def __init__(self, name):
self.thread_name = name
self.lock = threading.Lock() # 錯誤的聲明位置
super(Counter, self).__init__(name=name)
def run(self):
global count
for i in xrange(100000):
self.lock.acquire()
count = count + 1
self.lock.release()
counters = [Counter('thread:%s' % i) for i in range(5)]
for counter in counters:
print counter.thread_name
counter.start()
time.sleep(5)
print 'count=%s' % count
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python動態(tài)進(jìn)度條的實現(xiàn)代碼
有時候我們需要使用print打印工作進(jìn)度,正常使用print函數(shù)會導(dǎo)致刷屏的現(xiàn)象,本文通過實例代碼給大家介紹python動態(tài)進(jìn)度條的實現(xiàn)方法,感興趣的朋友跟隨小編一起看看吧2019-07-07
JPype實現(xiàn)在python中調(diào)用JAVA的實例
本篇文章主要介紹了JPype實現(xiàn)在python中調(diào)用JAVA的實例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
解決Cron定時任務(wù)中Pytest腳本無法發(fā)送郵件的問題
文章探討解決在 Cron 定時任務(wù)中運行 Pytest 腳本時郵件發(fā)送失敗的問題,先優(yōu)化環(huán)境變量,再檢查 Pytest 郵件配置,接著配置文件確保 SMTP 服務(wù)正常,包括編輯相關(guān)文件、配置認(rèn)證信息等,還提及常見問題排查,如防火墻等,最終使郵件功能在定時任務(wù)中成功運行2025-01-01
python 二維數(shù)組90度旋轉(zhuǎn)的方法
今天小編就為大家分享一篇python 二維數(shù)組90度旋轉(zhuǎn)的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01

