多線程
python提供了thread、threading和Queue模塊來支持多線程編程
thread只支持基本的功能,不推薦使用,threading是更加全面,更高級別的模塊,
使用Queue可以創建一個隊列數據結構,用於在多線程之間進行共享
threading
模塊
對象 | 描述 |
---|---|
Thread | 表示一個執行線程的對象 |
Lock | 鎖原語對象 |
RLock | 可重入鎖對象,使單一線程可以(在此)獲得已持有的鎖(遞歸鎖) |
Condition | 條件變量對象 |
Event | 條件變量的通用版本,等待的條件滿足後喚醒所有等待線程 |
Semaphone | 爲線程間共享的有限資源提供了一個“計數器”,沒有可用資源時阻塞 |
BoundeSemaphone | 與Semaphon類似,不過不允許超過初始值 |
Timer | 定時器,運行前會等待一段時間 |
Barrier | 創建一個“障礙”,必須達到指定數量的線程後纔可以繼續(PY3.2引入) |
屬性和方法
屬性 | 描述 |
---|---|
name | 線程名 |
ident | 線程標識符 |
daemon | bool值,表示這個線程是否是守護線程 |
__init__(group=None, tatget=None, name=None, args=(),kwargs ={}, verbose=None, daemon=None) | 實例化一個線程對象,需要有一個可調用的 target,以及其參數 args或 kwargs。還可以傳遞 name 或 group 參數,不過後者還未實現。此外, verbose 標 志 也 是 可 接 受 的。 而 daemon 的 值 將 會 設定thread.daemon 屬性/標誌 |
start() | 開啓線程,自動調用run()方法執行線程代碼 |
run() | 定義線程功能的方法,用來實現線程的功能與業務邏輯,一般在子類中重寫 |
join(timeout = None) | 等待線程結束後或者超時返回 |
is_alive() | bool值,表示這個線程是否還存活 |
active_count() | 當前活動的Thread對象個數 |
current_thread() | 返回當前的 Thread對象 |
enumerate() | 返回當前活動的Thread對象列表 |
設置守護線程daemon屬性:
1.daemon=True時主線程結束時不對該子線程進行檢查而直接退出,同時所有爲True的子線程無論是否運行完成都將隨主線程一起結束。
2.daemon=False時主線程結束時會檢查該子線程是否運行完成,若該子線程未完成,則主線程會等該子線程運行完成後再退出
多線程時,由於daemon的默認值爲False,主線程執行完自己的任務以後會檢查子線程是否運行完成,若子線程未完成,則主線程會等該子線程運行完成後再退出。如果需要修改daemon的值,則必須再調用start()方法啓動線程之前修改
創建線程
使用Thread類創建線程的方法:
1.創建一個Thead實例, 傳給他一個函數
2. 繼承Thread類並在子類中重寫__init__()和run()方法
3. 創建Thread實例, 傳給他一個可調用的類實例 (一般不用)
例一:
#!/usr/bin/python3
from threading import Thread
def func(name):
print(name)
t = Thread(target = func, args = ("hello world",)) #args的傳參須爲列表
t.start()
t.join()
互斥鎖
Threading.Lock
方法:
- acquire()
獲得鎖。該方法等待鎖被解鎖,將其設置爲locked並返回True。 - release() 釋放鎖。當鎖被鎖定時,將其重置爲解鎖並返回。如果鎖未鎖定,則會引發RuntimeError。
- locked() 如果鎖被鎖定,返回True
例二+互斥鎖:
from threading import Thread, Lock
exitFlag = 0
lock = Lock()
class myThread(Thread):
def __init__(self, ID, name, counter):
super().__init__() #調用父類__init__
self.id = ID
self.name = name # 設置name屬性
self.counter = counter
# self.daemon = True / False 設置是否爲守護線程
def run(self):
print("開始線程:" + self.name)
lock.acquire() #加鎖
print_time(self.name, self.counter, 3)
lock.release() #釋放鎖
print("Done")
def print_time(name, delay, counter):
while counter:
if exitFlag:
print("shutdone")
name.exit()
time.sleep(delay)
print("%s : %s " % (name, time.ctime(time.time())))
counter -= 1
thread_1 = myThread(3, "lzj", 3)
thread_2 = myThread(6, "wrh", 2)
thread_1.start()
thread_2.start()
thread_1.join()
thread_2.join()
條件變量
此段來自鏈接
threading.Condition
方法:
-
acquire() — 線程鎖,注意線程條件變量Condition中的所有相關函數使用必須在acquire() /release() 內部操作;
-
release() — 釋放鎖,注意線程條件變量Condition中的所有相關函數使用必須在acquire() /release() 內部操作;
-
wait(timeout) — 線程掛起(阻塞狀態),直到收到一個notify通知或者超時纔會被喚醒繼續運行(超時參數默認不設置,可選填,類型是浮點數,單位是秒)。wait()必須在已獲得Lock前提下才能調用,否則會觸發RuntimeError;
-
notify(n=1) — 通知其他線程,那些掛起的線程接到這個通知之後會開始運行,缺省參數,默認是通知一個正等待通知的線程,最多則喚醒n個等待的線程。notify()必須在已獲得Lock前提下才能調用,否則會觸發RuntimeError,notify()不會主動釋放Lock;
-
notifyAll() — 如果wait狀態線程比較多,notifyAll的作用就是通知所有線程
#! /usr/bin/python3
# 導入線程模塊
import threading
import time
# 創建條件變量condition
con = threading.Condition()
meat_num = 0
def thread_consumers():
# 條件變量condition 線程上鎖
con.acquire()
# 全局變量聲明關鍵字 global
global meat_num
meat_num = 0
# 等待肉片下鍋煮熟
con.wait()
while True:
print("我來一塊肉片...")
meat_num -= 1
print("剩餘肉片數量:%d"%meat_num)
time.sleep(0.5)
if meat_num == 0:
# 肉片吃光了,通知老闆添加肉片
print("老闆,再來一份老肉片...")
con.notify()
# 肉片吃光了,等待肉片
con.wait()
# 條件變量condition 線程釋放鎖
con.release()
def thread_producer():
# 條件變量condition 線程上鎖
con.acquire()
# 全局變量聲明關鍵字 global
global meat_num
# 肉片熟了,可以開始吃了
meat_num = 10
print("肉片熟了,可以開始吃了...")
con.notify()
while True:
# 阻塞函數,等待肉片吃完的通知
con.wait()
meat_num = 10
# 添加肉片完成,可以繼續開吃
print("添加肉片成功!當前肉片數量:%d"%meat_num)
time.sleep(1)
con.notify()
con.release()
if __name__ == "__main__":
# 創建並初始化線程
t1 = threading.Thread(target=thread_producer)
t2 = threading.Thread(target=thread_consumers)
# 啓動線程 -- 注意線程啓動順序,啓動順序很重要
t2.start()
t1.start()
# 阻塞主線程,等待子線程結束
t1.join()
t2.join()
print("程序結束!")
Queue
來自菜鳥
Queue
模塊是一個提供了同步的、線程安全的隊列類,包括FIFO(先入先出)隊列Queue,LIFO(後入先出)隊列LifoQueue,和優先級隊列 PriorityQueue
這些隊列都實現了鎖原語,能夠在多線程中直接使用,可以使用隊列來實現線程間的同步。
- Queue 模塊中的常用方法:
- Queue.qsize() 返回隊列的大小
- Queue.empty() 如果隊列爲空,返回True,反之False
- Queue.full() 如果隊列滿了,返回True,反之False
- Queue.full 與 maxsize 大小對應
- Queue.get([block[, timeout]]) 獲取隊列,timeout等待時間
- Queue.get_nowait() 相當Queue.get(False)
- Queue.put(item) 寫入隊列,timeout等待時間
- Queue.put_nowait(item) 相當Queue.put(item, False)
- Queue.task_done() 在完成一項工作之後,Queue.task_done()函數向任務已經完成的隊列發送一個信號
- Queue.join() 實際上意味着等到隊列爲空,再執行別的操作
#!/usr/bin/python3
import queue #引入queue模塊
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print ("開啓線程:" + self.name)
process_data(self.name, self.q)
print ("退出線程:" + self.name)
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire() #互斥鎖加鎖,取得任務的
if not workQueue.empty():
data = q.get()
queueLock.release() #解鎖
print ("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock() #創建互斥鎖和隊列對象
workQueue = queue.Queue(10)
threads = []
threadID = 1
# 創建新線程
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# 加鎖,填充隊列
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# 等待隊列清空
while not workQueue.empty():
pass
# 通知線程是時候退出
exitFlag = 1
# 等待所有線程完成
for t in threads:
t.join()
print ("退出主線程")
添加、取出元素要加鎖