如何使用Python中的锁来保护对象?

时间:2016-08-16 15:19:10

标签: python multithreading locks

我遇到了需要以下模式的功能:

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")

但它感觉非常基本,它应该存在于标准库中,或者它是一种非常规的使用锁的方式。

3 个答案:

答案 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类的实现意味着此代码是否是线程安全的。例如,如果objlist,那么这是安全的,因为obj[:]会创建副本。但是,如果objnumpy.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