在Python中临时释放占用的锁
我有一些不同的方法,它们不应该同时运行,所以我用一个锁来同步它们。大概是这样的:
selected_method = choose_method()
with lock:
selected_method()
在这些方法中,有时候我会调用一个辅助函数,这个函数会进行一些比较慢的网络操作。我们就叫这个函数为 network_method()
。我希望在这个函数运行的时候释放锁,这样其他线程就可以继续处理它们的事情。
一种实现方式是,在调用网络方法之前先调用 lock.release() 来释放锁,等网络方法调用完后再用 lock.acquire() 来重新获取锁。不过,我更希望这些方法不需要知道锁的存在,因为它们有很多,而且还经常会变。
我更想重写 network_method()
,让它先检查一下锁是否被占用,如果被占用就释放锁,然后在结束时再重新获取锁。
需要注意的是,network_method()
有时候会从其他地方被调用,所以如果不是在持有锁的线程中,就不应该释放锁。
我试过在锁对象上使用 locked()
方法,但这个方法只能告诉我锁是否被占用,而不能告诉我它是否被当前线程占用。
顺便提一下,锁是一个全局对象,我对此没有问题。
2 个回答
为什么不直接这样做呢?
with lock:
before_network()
do_network_stuff()
with lock:
after_network()
我更希望重写network_method(),让它先检查一下锁是否被占用,如果被占用就先释放锁,然后再开始执行,最后再重新获取锁。
需要注意的是,network_method()有时会在其他地方被调用,所以如果不是在持有锁的线程中,就不应该释放锁。
这听起来完全是错误的做法 :(
首先,函数有时会根据调用它的地方产生一些神秘的副作用,这样的设计是很糟糕的。这种情况调试起来简直是一场噩梦。
其次,锁的获取和释放应该是明确的。如果我看到代码是“lock(); do_something(); unlock();”,我就会期待在执行do_something()的过程中锁是被占用的。实际上,这也在告诉我do_something()需要一个锁。如果我发现有人写了一个特定的do_something(),它实际上解锁了我刚才看到的锁,我会选择(a) 解雇他们,或者(b) 根据我在他们面前的职位高低来追究他们。
顺便说一下,锁是一个全局对象,我对此没意见。
顺便提一下,这也是为什么全局变量不好。如果我修改一个值,调用一个函数,然后再修改一个值,我不希望中间的那个函数能以不可预测的方式回过头来修改这个值。
我给你的建议是:你的锁放错地方了,或者做错了事情,或者两者都有。你说这些方法不应该同时运行,但实际上你希望其中一些能够并行运行。一个方法“慢”并不能让你就可以去掉锁——要么在这种操作中你需要互斥锁以确保正确性,要么你不需要。如果这个慢的操作在其他操作不安全的情况下确实是安全的,那可能它就不需要锁——但这意味着锁应该放在每个快速操作内部,而不是外部。不过这一切都取决于锁到底是用来做什么的。