python學習筆記五:併發


多線程

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
方法:

  1. acquire()
    獲得鎖。該方法等待鎖被解鎖,將其設置爲locked並返回True。
  2. release() 釋放鎖。當鎖被鎖定時,將其重置爲解鎖並返回。如果鎖未鎖定,則會引發RuntimeError。
  3. 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
方法:

  1. acquire() — 線程鎖,注意線程條件變量Condition中的所有相關函數使用必須在acquire() /release() 內部操作;

  2. release() — 釋放鎖,注意線程條件變量Condition中的所有相關函數使用必須在acquire() /release() 內部操作;

  3. wait(timeout) — 線程掛起(阻塞狀態),直到收到一個notify通知或者超時纔會被喚醒繼續運行(超時參數默認不設置,可選填,類型是浮點數,單位是秒)。wait()必須在已獲得Lock前提下才能調用,否則會觸發RuntimeError;

  4. notify(n=1) — 通知其他線程,那些掛起的線程接到這個通知之後會開始運行,缺省參數,默認是通知一個正等待通知的線程,最多則喚醒n個等待的線程。notify()必須在已獲得Lock前提下才能調用,否則會觸發RuntimeError,notify()不會主動釋放Lock;

  5. 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 模塊中的常用方法:
  1. Queue.qsize() 返回隊列的大小
  2. Queue.empty() 如果隊列爲空,返回True,反之False
  3. Queue.full() 如果隊列滿了,返回True,反之False
  4. Queue.full 與 maxsize 大小對應
  5. Queue.get([block[, timeout]]) 獲取隊列,timeout等待時間
  6. Queue.get_nowait() 相當Queue.get(False)
  7. Queue.put(item) 寫入隊列,timeout等待時間
  8. Queue.put_nowait(item) 相當Queue.put(item, False)
  9. Queue.task_done() 在完成一項工作之後,Queue.task_done()函數向任務已經完成的隊列發送一個信號
  10. 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 ("退出主線程")

添加、取出元素要加鎖

多進程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章