Python提供了一系列的对象支持线程/进程间的通讯:
- Lock
- RLock
- Condition
- Semaphone
- BounderSemaphone
- Event
- 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次的锁。