Python的線程/進程間通訊對象分析

Python提供了一系列的對象支持線程/進程間的通訊:

  1. Lock
  2. RLock
  3. Condition
  4. Semaphone
  5. BounderSemaphone
  6. Event
  7. Barrier

除了Lock,Rlock外,進程的相關對象只是線程相關對象的clone,而且進程的Lock,RLock也是與線程Lock,RLock相對應的對象。在這裏我們只分析線程的這幾種對象。
一般使用範圍:

Lock用於對互斥操作(單一資源,全局變量)
RLock與Lock類似,區別僅在與RLock在同一個線程可以多次獲取
Semaphone/BounderSemaphone用於對多個資源的申請使用, 如果BounderSemaphone(1)則==Lock()
Condition用於在等待某種事情發生
Event實際上是對Condition的一種操作簡化包裝,也更符合事件驅動的概念。
這幾種對象大概使用上面這些對象所要付出的開銷是不同的,我們從其原理來進行分析。

from time import sleep
from threading import Thread, Lock

class MyThread(Thread):
    def __init__(self, name, lock):
        super(MyThread, self).__init__(name=name)
        self.lock = lock

    def run(self):
        with self.lock:
            print('my name is %s, i will sleep 2 seconds' % self.name)
            sleep(2)
            print('i wake up now.')

if __name__ == '__main__':
    lock = Lock()
    mt1 = MyThread('t1', lock)
    mt2 = MyThread('t2', lock)
    mt1.start(); mt2.start()
    mt1.join(); mt2.join()
    print('main thread end!')

Lock的使用方法參考上面的代碼,而RLock是跟Lock的區別是如果在本線程已經acquire,則可以多次acquire,不同線程下則跟Lock是一致的;我們來看一下RLock的實現:

class _RLock:
    def __init__(self):
        self._block = _allocate_lock()
        self._owner = None
        self._count = 0

    def acquire(self, blocking=True, timeout=-1):
        me = get_ident()   # 獲取當前thread的identity
        if self._owner == me:  # 鎖的所有者是自己
            self._count += 1
            return 1
        rc = self._block.acquire(blocking, timeout)   #獲取鎖
        if rc:
            self._owner = me
            self._count = 1
        return rc

從上面的代碼可以看出,RLock實際上是使用了Lock,只是在acquire時判斷了是否是本線程,如果是,則記錄lock次數不做阻塞返回。

而Condition在調用wait時則新生成了一個Lock,並acquire了兩次已達到阻塞的目的,而。

class Condition:
    def __init__(self, lock=None):
        .....
        self._waiters = _deque()  # 初始化waiter Lock 隊列
    def wait(self, timeout=None):
        if not self._is_owned():  # 必須要先獲取鎖
            raise RuntimeError("cannot wait on un-acquired lock")
        waiter = _allocate_lock()
        waiter.acquire()
        self._waiters.append(waiter)  #將waiter Lock加入waiter隊列
        .....
        waiter.acquire()
    def _is_owned(self):
        # Return True if lock is owned by current_thread.
        # This method is called only if _lock doesn't have _is_owned().
        if self._lock.acquire(0):
            self._lock.release()   # 在wait,notify時會釋放互斥鎖
            return False
        else:
            return True
    def notify(self, n=1):
        if not self._is_owned():  # 必須要先獲取鎖
            raise RuntimeError("cannot notify on un-acquired lock")
        all_waiters = self._waiters
        waiters_to_notify = _deque(_islice(all_waiters, n))
        if not waiters_to_notify:
            return
        for waiter in waiters_to_notify:
            waiter.release()      #根據通知數量依次釋放

Lock只是一個單純的互斥體,而Condition則可在某種條件發生後主動通知正在等待某種條件的線程。

from threading import Thread, Lock, Condition

class MyWaitThread(Thread):
    def __init__(self, name, cond):
        super(MyWaitThread, self).__init__(name=name)
        # self.name = name
        self.cond = cond

    def run(self):
        with self.cond:
            print('I am waiting something happen!')
            self.cond.wait()
            print('wait end!')

class MyNotifyThread(Thread):
    def __init__(self, name, cond):
        super(MyNotifyThread, self).__init__(name=name)
        # self.name = name
        self.cond = cond

    def run(self):
        with self.cond:
            print('I am notifying all the wait thread.')
            self.cond.notify_all()
            print('notify end.')

if __name__ == '__main__':
    cond = Condition()
    mt1 = MyWaitThread('t1', cond)
    mt2 = MyWaitThread('t2', cond)
    mt3 = MyNotifyThread('t3', cond)
    mt1.start(); mt2.start(); mt3.start()
    mt1.join(); mt2.join();mt3.join()
    print('main thread end!')

Semaphone使用Condition的wait,notify來實現,但是卻可以看成一個可以在不同線程中同時獲取N次的鎖。

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