python的多線程基礎設施

當我們在使用線程時,存在以下基本的多線程編程的概念:

  • lock:多個線程訪問臨界資源時,爲滿足線程安全必須保證訪問臨界資源的代碼同一時刻僅有一個線程執行。
  • condition:傳遞消息的工具。線程中的條件,不滿足條件就wait,獲得條件就執行。
  • wait():在條件實例中可用的wait()。
  • notify() / notifyAll():在條件實例中可用的notify()通知一個其他等待的線程或其他所有線程,看當前線程對臨界資源狀態的改變是否與所有線程有關,一般只需要通知一個其他線程即可。

python提供的多線程基礎設施與其他語言的類似,都是在上述線程環境下的實現。下面是多線程實現的生產者消費者模型。

1、鎖的使用

from threading import Thread, Lock
import time
import random

queue = []
lock = Lock()

class ProducerThread(Thread):
    def run(self):
        nums = range(5)
        while True:
            num = random.choice(nums)
            lock.acquire()
            queue.append(num)
            print "Produced", num 
            lock.release()
            time.sleep(random.random())

class ConsumerThread(Thread):
    def run(self):
        while True:
            lock.acquire()
            if not queue:
                print "Nothing in queue, but consumer will try to consume"
            num = queue.pop(0)
            print "Consumed", num 
            lock.release()
            time.sleep(random.random())

ProducerThread().start()
ConsumerThread().start()

lock提供的release和lock方法將對臨界資源queue的訪問代碼進行了保護,使得不會存在同時訪問臨界資源的問題。但是,僅僅使用鎖會出現問題,因爲多個線程之間需要傳遞消息,(注意,傳遞數據使用全局變量臨界資源就可以,但傳遞消息必須要新的工具),需要使用線程傳遞消息的工具condition來實現。

2、傳遞消息

python的多線程傳遞消息機制condition內含了lock,其acquire()和release()方法在內部調用了lock的acquire()和release()。所以在python中可以用condiction實例取代lock實例,但lock的行爲不會改變。

from threading import Thread, Condition
import random
import time

queue = []
MAX_NUM = 10
queue_avalible = Condition()

class Producer(Thread):
    def run(self):
        nums = range(MAX_NUM)
        while True:
            num = random.choice(nums)
            queue_avalible.acquire()
            if len(queue) == MAX_NUM:
                print "queue is full, waiting for consuming"
                queue_avalible.wait()
            queue.append(num)
            print "Produced ", num
            queue_avalible.notify()
            queue_avalible.release()

class Consumer(Thread):
    def run(self):
        nums = range(MAX_NUM)
        while True:
            queue_avalible.acquire()
            if len(queue) == 0:
                print "queue is empty, waiting for producing"
                queue_avalible.wait()
            num = queue.pop(0)
            print "Consumed ", num
            queue_avalible.notify()
            queue_avalible.release()

Producer().start()
Consumer().start()

上述使用的是Condition內部自帶的lock來進行加鎖解鎖,但是這樣有一個需要注意的問題,調用notify的時候,其他等待的線程並不能馬上運行,因爲使用的是同一個queue_avalible,當前調用notify之後再調用release之後其他等待線程才能運行。下面是python文檔的原文:

Note: the notify() and notifyAll() methods don’t release the lock; this means that the thread or threads awakened will not return from their wait() call immediately, but only when the thread that called notify() or notifyAll() finally relinquishes ownership of the lock.
An awakened thread does not actually return from its wait() call until it can reacquire the lock. Since notify() does not release the lock, its caller should.

3、Queue封裝

python中的Queue模塊對多線程操作的隊列進行了封裝,非常方便的使用它能快速構建程序。

The Queue module implements multi-producer, multi-consumer queues. It is especially useful in threaded programming when information must be exchanged safely between multiple threads. The Queue class in this module implements all the required locking semantics.

支持如下三種隊列:

  • class Queue.Queue(maxsize=0)
    FIFO隊列類。 maxsize 是最大長度,達到上限之後調用put操作會被阻塞。小於等於0的maxsize將是無限大的隊列。
  • class Queue.LifoQueue(maxsize=0)
    LIFO隊列類。 maxsize 是最大長度,達到上限之後調用put操作會被阻塞。小於等於0的maxsize將是無限大的隊列。
  • class Queue.PriorityQueue(maxsize=0)
    優先隊列類。 maxsize 是最大長度,達到上限之後調用put操作會被阻塞。小於等於0的maxsize將是無限大的隊列。

另外提供兩種異常:
Queue.Empty
當隊列爲空是,調用了non-blocking get() (or get_nowait()) 函數時發生
- Queue.Full
當隊列滿之後,調用了 non-blocking put() (or put_nowait()) 函數時發生

提供的方法如下:
- Queue.qsize():返回隊列大小
- Queue.empty()
- Queue.full()
- Queue.get([block[, timeout]]):獲取一個值,如果block爲true並且timeout爲None,就會在隊列爲空時阻塞只到有元素;如果timeout爲正整數,將會最多阻塞設置的時間,然後raises Empty exception。如果block爲False,那麼直接在有元素時返回該元素,否則直接拋出Empty異常。
- Queue.get_nowait():相當於get(false)
- Queue.put(item[, block[, timeout]]):插入一個值,如果block爲true並且timeout爲None,就會在隊列滿了之後阻塞只到有空閒位置;如果timeout爲正整數,將會最多阻塞設置的時間,然後raises Full exception。如果block爲False,那麼直接在有空閒位置時插入,否則直接拋出Full異常。
- Queue.put_nowait(item):相當於put(item, false)
- Queue.task_done():檢查後臺線程是否完成
- Queue.join():等待後臺線程完成

發佈了158 篇原創文章 · 獲贊 42 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章