Python 线程间的同步机制2:Rlock 和 Condition

Rlock

我们前面说过了Lock,python的threading库中还提供了一个可重入锁Rlock。

还记得吗,对于Lock,我们不能说Lock被某个线程持有,或Lock属于某个线程,因为一个线程使用acquire使锁进入locked态,任何线程都可以调用release把锁“打开”——恢复unlocked态。

这里的Rlock相比于Lock,增加了两个特性。

1.Rlock锁是被某个线程持有的(owned by the thread that locked it
线程T1调用了acquire时,只有T1可以调用release使Rlock回到unlocked状态。可以这么理解Rlock被T1线程抢回家了,只要T1线程不放人,别的线程想要acquire的话都会阻塞(同上一篇,这里不讨论 blocking = False的情况)。

2.Rlock 是可重入的锁,有n层acquire就必须有n层release才能释放锁。

Condition

Condition 条件变量——锁的一种高级形式。
Condition 可以让多个线程被挂起,直到另外的某个线程调用notifynotify_all方法发出“放行”信号,之前挂起的若干个或所有线程会被放行。

Condition实现线程间同步是有锁参与的,默认的内置锁是RLock

.wait(timeout=None)方法
执行.wait()会自动执行一次内置锁的release()然后挂起,这也提醒了我们执行.wait()前这个线程必须acquire()到了内置锁,否则会报RuntimeError
要注意:阻塞不会在收到notify()notify_all信号后立刻结束,因为wait()会尝试重新获取锁,所以必须要等nofity的线程手动释放锁。

.notify(n=1)方法
执行.notify()前这个线程必须acquire()到了内置锁,否则会报RuntimeError
发出放行信号,放行n个等待线程,.notify_all()则会放行所有等待者。但要注意notify()方法本身不会自动release,还需要手动调用release放弃锁的所有权,才会真正让等待者开始执行。

.wait_for(predicate, timeout=None)方法
更加实用,比如在一个消费者生产者模型中,消费者遇到库存为0的时候会wait,生产者补充100单位库存后,发出notify_all信号,使得所有wait中的顾客都被激活。

但这些顾客是否一定能买到东西呢?答案是不一定。有可能第一位顾客就是个大款,直接买光了100单位,后面的顾客已经被唤醒,但库存仍然为0,如果此时不增加一个确认机制的话,库存很可能就被买负了。

wait_for就提供了一个确认机制:当predicate参数为False时,持续wait。
不考虑time out超时自动放行的话,相当于执行了:

While not predicate:
	condition.wait()

使得每一次wait被激活后,往下执行之前还会再确认一次predicate是否为True。有货才购买,无货只能继续等待。

上一篇:【C#随手记】FTP上传与下载


下一篇:python3 GIL锁/互斥锁Lock和递归锁Rlock