我遇到了需要以下模式的功能:
from threading import Lock
the_list = []
the_list_lock = Lock()
并使用它:
with the_list_lock:
the_list.append("New Element")
不幸的是,这并不需要我获取锁,我可以直接访问该对象。我希望得到一些保护(我只是人类。)有没有一种标准的方法来做到这一点?我自己的方法是创建一个HidingLock
类,可以像这样使用:
the_list = HidingLock([])
with the_list as l:
l.append("New Element")
但它感觉非常基本,它应该存在于标准库中,或者它是一种非常规的使用锁的方式。
答案 0 :(得分:6)
我认为标准库中没有任何东西的原因是因为它存在,它需要制作铸铁访问保证。提供更少的东西会产生错误的安全感,这可能导致同样多的并发问题。
在没有做出实质性的牺牲的情况下,几乎不可能做出这些保证。因此,用户需要考虑如何管理并发问题。这符合Python的一个哲学理念,我们都是成年人同意的#34;。也就是说,如果您正在编写一个类,我认为在访问该属性之前您应该知道获取锁所需的属性是合理的。或者,如果您真的那么关心,请编写一个包装器/代理类来控制对底层对象的所有访问。
通过您的示例,目标对象可能会以多种方式意外逃脱。如果程序员没有足够重视他们正在编写/维护的代码,那么这个HiddenLock
可能会提供错误的安全感。例如:
with the_lock as obj:
pass
obj.func() # erroneous
with the_lock as obj:
return obj.func() # possibly erroneous
# What if the return value of `func' contains a self reference?
with the_lock as obj:
obj_copy = obj[:]
obj_copy[0] = 2 # erroneous?
最后一个特别有害。此代码是否是线程安全的,不取决于with块中的代码,甚至取决于块之后的代码。相反,它是obj
类的实现意味着此代码是否是线程安全的。例如,如果obj
是list
,那么这是安全的,因为obj[:]
会创建副本。但是,如果obj
是numpy.ndarray
,那么obj[:]
会创建一个视图,因此操作不安全。
实际上,如果obj
的内容是可变的,那么这可能是不安全的(例如。obj_copy[0].mutate()
)。
答案 1 :(得分:0)
我目前的解决方案(我在问题中谈到的那个)看起来像这样:
import threading
class HidingLock(object):
def __init__(self, obj, lock=None):
self.lock = lock or threading.RLock()
self._obj = obj
def __enter__(self):
self.lock.acquire()
return self._obj
def __exit__(self, exc_type, exc_value, traceback):
self.lock.release()
def set(self, obj):
with self:
self._obj = obj
以下是人们如何使用它:
locked_list = HidingLock(["A"])
with locked_list as l:
l.append("B")
答案 2 :(得分:0)
如何使用shared_list
创建具有list
并使用threading.Lock
实现所需类方法的import threading
class SharedList(object):
def __init__(self, iterable=None):
if iterable is not None:
self.list = list(iterable)
else:
self.list = list()
self.lock = threading.Lock()
self.index = None
def append(self, x):
with self.lock:
self.list.append(x)
def __iter__(self):
shared_iterator = SharedList()
shared_iterator.list = self.list
shared_iterator.lock = self.lock
shared_iterator.index = 0
return shared_iterator
def next(self):
with self.lock:
if self.index < len(self.list):
result = self.list[self.index]
self.index += 1
else:
raise StopIteration
return result
# Override other methods
if __name__ == '__main__':
shared_list = SharedList()
for x in range(1, 4):
shared_list.append(x)
for entry in shared_list:
print entry
:
1
2
3
<强>输出强>
the_list = SharedList()
the_list.append("New Element")
正如Georg Shölly在评论中指出的那样,这需要大量的工作来实现每个方法。但是,如果你需要的只是一个列表,你可以追加,然后迭代,这个例子提供了起点。
然后你可以写
fooUri