❶ python基礎(21)-線程通信
到這里,我們要聊一下線程通信的內容;
首先,我們拋開語言不談,先看看比較基礎的東西,線程間通信的方式;其實也就是哪幾種(我這里說的,是我的所謂的知道的。。。)事件,消息隊列,信號量,條件變數(鎖算不算?我只是認為是同步的一種);所以我們也就是要把這些掌握了,因為各有各的好處嘛;
條件變數我放到了上面的線程同步裡面講了,我總感覺這算是同步的一種,沒有很多具體信息的溝通;同時吧,我認為條件變數比較重要,因為這種可以應用於線程池的操作上;所以比較重要;這里,拋開條件變數不談,我們看看其他的東西;
1、消息隊列:
queue 模塊下提供了幾個阻塞隊列,這些隊列主要用於實現線程通信。在 queue 模塊下主要提供了三個類,分別代表三種隊列,它們的主要區別就在於進隊列、出隊列的不同。
關於這三個隊列類的簡單介紹如下:
queue.Queue(maxsize=0):代表 FIFO(先進先出)的常規隊列,maxsize 可以限制隊列的大小。如果隊列的大小達到隊列的上限,就會加鎖,再次加入元素時就會被阻塞,直到隊列中的元素被消費。如果將 maxsize 設置為 0 或負數,則該隊列的大小就是無限制的。
queue.LifoQueue(maxsize=0):代表 LIFO(後進先出)的隊列,與 Queue 的區別就是出隊列的順序不同。
PriorityQueue(maxsize=0):代表優先順序隊列,優先順序最小的元素先出隊列。
這三個隊列類的屬性和方法基本相同, 它們都提供了如下屬性和方法:
Queue.qsize():返回隊列的實際大小,也就是該隊列中包含幾個元素。
Queue.empty():判斷隊列是否為空。
Queue.full():判斷隊列是否已滿。
Queue.put(item, block=True, timeout=None):向隊列中放入元素。如果隊列己滿,且 block 參數為 True(阻塞),當前線程被阻塞,timeout 指定阻塞時間,如果將 timeout 設置為 None,則代表一直阻塞,直到該隊列的元素被消費;如果隊列己滿,且 block 參數為 False(不阻塞),則直接引發 queue.FULL 異常。
Queue.put_nowait(item):向隊列中放入元素,不阻塞。相當於在上一個方法中將 block 參數設置為 False。
Queue.get(item, block=True, timeout=None):從隊列中取出元素(消費元素)。如果隊列已滿,且 block 參數為 True(阻塞),當前線程被阻塞,timeout 指定阻塞時間,如果將 timeout 設置為 None,則代表一直阻塞,直到有元素被放入隊列中; 如果隊列己空,且 block 參數為 False(不阻塞),則直接引發 queue.EMPTY 異常。
Queue.get_nowait(item):從隊列中取出元素,不阻塞。相當於在上一個方法中將 block 參數設置為 False。
其實我們想想,這個隊列,是python進行封裝的,那麼我們可以用在線程間的通信;同時也是可以用做一個數據結構;先進先出就是隊列,後進先出就是棧;我們用這個棧寫個十進制轉二進制的例子:
沒毛病,可以正常的列印;其中需要注意的就是,maxsize在初始化的時候如果是0或者是個負數的話,那麼就會是不限制大小;
那麼其實我們想想,我們如果用做線程通信的話,我們兩個線程,可以把隊列設置為1的大小,如果是1對多,比如是創建者和消費者的關系,我們完全可以作為消息隊列,比如說創建者一直在創建一些東西,然後放入到消息隊列裡面,然後供消費著使用;就是一個很好的例子;所以,其實說是消息隊列,也就是隊列,沒差;
=====================================================================
下面來看一下事件
Event 是一種非常簡單的線程通信機制,一個線程發出一個 Event,另一個線程可通過該 Event 被觸發。
Event 本身管理一個內部旗標,程序可以通過 Event 的 set() 方法將該旗標設置為 True,也可以調用 clear() 方法將該旗標設置為 False。程序可以調用 wait() 方法來阻塞當前線程,直到 Event 的內部旗標被設置為 True。
Event 提供了如下方法:
is_set():該方法返回 Event 的內部旗標是否為True。
set():該方法將會把 Event 的內部旗標設置為 True,並喚醒所有處於等待狀態的線程。
clear():該方法將 Event 的內部旗標設置為 False,通常接下來會調用 wait() 方法來阻塞當前線程。
wait(timeout=None):該方法會阻塞當前線程。
這里我想解釋一下;其實對於事件來說,事件可以看成和條件變數是一樣的,只是我們說說不一樣的地方;
1、對於事件來說,一旦觸發了事件,也就是說,一旦set為true了,那麼就會一直為true,需要clear調內部的標志,才能繼續wait;但是conditon不是,他是一次性的喚醒其他線程;
2、conditon自己帶鎖;事件呢?不是的;沒有自己的鎖;比如說有一個存錢的線程,有一個是取錢的線程;那麼存錢的線程要存錢;需要怎麼辦呢?1、發現銀行沒有錢了(is_set判斷);2、鎖住銀行;3、存錢;4、釋放銀行;5、喚醒事件;對於取錢的人;1、判斷是否有錢;2、被喚醒了,然後鎖住銀行;3、開始取錢;4、清理告訴存錢的人,我沒錢了(clear);5、釋放鎖;6、等著錢存進去;
其實說白了,就是記住一點;這個旗標需要自己clear就對了
寫個例子,怕以後忘了怎麼用;
其實時間和信號量比較像;但是信號量不用自己清除標志位;但是事件是需要的;
❷ [python] 線程間同步之信號量Semaphore
信號量 semaphore 是用於控制進入數量的鎖。有哪些應用場景呢,比如說在讀寫文件的時候,一般只能只有一個線程在寫,禪掘斗而讀可以有多個線程同時進行,如果需要限制同時讀文件的線程個數,這時候就可以用到信號量了(如果用互斥鎖,就是限制同一時刻只能有一散畢個線程讀取文件)。又比如在做爬蟲的時候,有時候爬取速度太快了,會導致被網站禁止,所以這個時候就需要控制爬蟲爬取網站的頻率。
semaphore 內部維護了一個條件變數 condition ,構造函數是:
主要有兩個方法:
用爬蟲來舉例,假如說有一個 UrlProcer 線程,爬取 url ,多個 htmlSpider 線程,爬取 url 對應的網頁。如果直接開20個 htmlSpider 線程,20個線程是同時執行的賀磨,現在要限制同時執行能執行三個,就可以使用信號量來控制:
從結果可以看出,每次都幾乎是三個同時的完成任務。
❸ python 線程同步問題
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print "Starting " + self.name
# 獲得鎖,成功獲得鎖定後返回True
# 可選的timeout參數不填時將一直阻塞直到獲得鎖定
# 否則超時後將返回False
threadLock.acquire()
print_time(self.name, self.counter, 3)
# 釋放鎖
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print "%s: %s" % (threadName, time.ctime(time.time()))
counter -= 1
threadLock = threading.Lock()
threads = []
# 創建新線程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# 開啟新線程
thread1.start()
thread2.start()
❹ python多線程
有很多的場景中的事情是同時進行的,比如開車的時候,手和腳共同來駕駛汽車,再比如唱歌跳舞也是同時進行的
結果:
• _thread
• threading(推薦使用)
結果:
threading.enumerate() 可查看當前正在運行的線程
結果:
結果:
結果:
結果: 出現資源競爭導致計算結果不正確
(1)當多個線程幾乎同時修改某一個共享數據的時候,需要進行同步控制
(2)線程同步能夠保證多個線程安全訪問資源,最簡單的同步機制是引入互斥鎖
(3)互斥鎖為資源引入一個狀態: 鎖定/非鎖定
(4)某個線程要更愛共享數據時,先將其鎖定,此時資源的狀態為"鎖定", 其他線程不能更改;直到該線程釋放資源,將資源狀態變為"非鎖定"
(5)互斥鎖保證了每次只有一個線程進行寫入操作,從而保證了多線程情況下數據的正確性
結果: 計算正確
結果:卡住了
在線程間共享多個資源的時候,如果兩個線程分別戰友一部分資源且同時等待對方資源,就會造成死鎖
(1)程序設計時避免(銀行家演算法)
(2)添加超時時間
❺ PYTHON多線程同步的幾種方法
Python進階(二十六)-多線程實現同步的四種方式
臨界資源即那些一次只能被一個線程訪問的資源,典型例子就是列印機,它一次只能被一個程序用來執行猜顫列印功能,因為不能多個線程同時操作,而訪問這部分資源的代碼通常稱之為搜配臨界區。
鎖機制
threading的Lock類,用該類的acquire函數進行加鎖,用realease函數進行解鎖
import threadingimport timeclass Num:
def __init__(self):
self.num = 0
self.lock = threading.Lock() def add(self):
self.lock.acquire()#加鎖,鎖住相應的資源
self.num += 1
num = self.num
self.lock.release()#解鎖,離開該資源
return num
n = Num()class jdThread(threading.Thread):
def __init__(self,item):
threading.Thread.__init__(self)
self.item = item def run(self):
time.sleep(2)
value = n.add()#將num加1,並輸出原來的數據和+1之後的數據
print(self.item,value)for item in range(5):
t = jdThread(item)
t.start()
t.join()#使線程一個一個執行
當一個線程調用鎖的acquire()方法獲得鎖時,鎖就進入「locked」狀態。每次只有一個線程可以獲得鎖。如果此時另一個線程試圖獲得這個鎖世兆指,該線程就會變為「blocked」狀態,稱為「同步阻塞」(參見多線程的基本概念)。
直到擁有鎖的線程調用鎖的release()方法釋放鎖之後,鎖進入「unlocked」狀態。線程調度程序從處於同步阻塞狀態的線程中選擇一個來獲得鎖,並使得該線程進入運行(running)狀態。
信號量
信號量也提供acquire方法和release方法,每當調用acquire方法的時候,如果內部計數器大於0,則將其減1,如果內部計數器等於0,則會阻塞該線程,知道有線程調用了release方法將內部計數器更新到大於1位置。
import threadingimport timeclass Num:
def __init__(self):
self.num = 0
self.sem = threading.Semaphore(value = 3) #允許最多三個線程同時訪問資源
def add(self):
self.sem.acquire()#內部計數器減1
self.num += 1
num = self.num
self.sem.release()#內部計數器加1
return num
n = Num()class jdThread(threading.Thread):
def __init__(self,item):
threading.Thread.__init__(self)
self.item = item def run(self):
time.sleep(2)
value = n.add()
print(self.item,value)for item in range(100):
❻ python進程和線程中的join方法
python中創建進程的方式
一、Process(target=函數名,args=(),name,kwargs)
target:加進程調用的函數名,一般不加括弧
name:進程的名字
kwargs:字典參數
args:元組參數,如果參數就一個,記得加逗號』,』
Python多線程與多進程中join()方法的效果是相同的
join所完成的工作就是線程同步,即主線程任務結束之後,進入阻塞狀態,一直等待其他的子線程執行結束之後,主線程再終止
import threading
import time
❼ 關於python多線程的一些問題。
創建的子線程默認是非守護的。
非守護:當主線程結束時,子線程繼續運行,二者互不影響。
子線程是守護線程:當主線程結束時,子線程也結束(不管子線程工作有沒有完成)。
join作用是線程同步,是讓主線程等待子線程結束才結束(主線程完成工作了也不結束,阻塞等待,等子線程完成其工作才一起結束)。
相信此時你已經懂你的兩個問題了。
沒加join的時候主線程結束了,所以命令提示符>>>就出來了,可是子線程還沒結束,過了3/5秒後列印了字元串。加了join後主線程等兩個子線程都結束才一起結束,所以最後才出來>>>。
理解確實有點偏差。守護是指子線程守護著主線程,你死我也死,謂之守護。