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

2024-10-01 15:33:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我已经获得了一些需要以下模式的功能:

from threading import Lock

the_list = []
the_list_lock = Lock()

使用它:

^{pr2}$

不幸的是,这不需要我获取锁,我可以直接访问对象。我想要一些保护措施(我只是人类)有标准的方法吗?我自己的方法是创建一个HidingLock类,可以这样使用:

the_list = HidingLock([])
with the_list as l:
    l.append("New Element")

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


Tags: the对象方法fromimport功能lock标准
3条回答

创建一个具有list并使用threading.Lock实现所需的类方法的shared_list怎么样:

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

输出

^{pr2}$

正如Georg Shölly在评论中指出的,这将需要大量的工作来实现每个方法。但是,如果您所需要的只是一个可以附加到然后迭代的列表,那么这个示例提供了一个起点。在

那你就可以写了

the_list = SharedList()
the_list.append("New Element")

我认为标准库中什么都没有的原因是,要想让它在那里,就需要做出铸铁般的访问保证。提供更少的内容会给人一种错误的安全感,这可能会导致同样多的并发问题。在

如果不做出实质性的性能牺牲,几乎不可能做出这些保证。因此,由用户来考虑如何管理并发问题。这与Python的“我们都是同意的成年人”的哲学思想相一致。也就是说,如果你在写一个类,我认为在访问这个属性之前,你应该知道你需要获取一个锁的属性。或者,如果你真的那么担心,写一个包装器/代理类来控制对底层对象的所有访问。在

在您的例子中,有许多方法可以使目标对象意外地逃逸。如果程序员没有对他们正在编写/维护的代码给予足够的关注,那么这个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())。在

我目前的解决方案(我在问题中谈到的)如下:

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

下面是如何使用它:

^{pr2}$

相关问题 更多 >

    热门问题