【问题标题】:moving thread-synchronisation-responisbility into shared resource将线程同步责任转移到共享资源中
【发布时间】:2014-10-02 22:18:30
【问题描述】:

给定一个由多个线程使用的对象(=实例),以下方法是最常见的(据我所知):

共享资源:

class Resource():
    def return_some_value(self):
      return self.somevalue

全局(或父上下文):

lock = Lock()
res = Resource()

在访问线程中:

lock.acquire()
res.return_some_value()
lock.release()

显然访问线程负责锁定和解锁共享资源。

假设共享资源更复杂,一些方法/属性是只读的,而另一些则不是。使用资源的线程现在必须知道哪个是哪个并相应地锁定。这显然容易出错,而且我有时无法选择将 Lock 对象放在何处。

现在我的问题是:是否可以将正确锁定共享资源本身的责任转移?它甚至有意义吗? 比如:

class Resource():
    def __init__(self):
        self.lock = Lock()
    def return_some_value(self):
        self.lock.acquire()
        return self.somevalue
        self.lock.release() # obviously this won't work

【问题讨论】:

    标签: python multithreading thread-safety locking


    【解决方案1】:

    是的,这样做非常有意义。您也可以use a Lock as a context manager 以确保它始终发布:

    class Resource():
        def __init__(self):
            self.lock = Lock()
    
        def return_some_value(self):
            with self.lock:  # Lock gets acquired
                return self.somevalue  # Lock gets released
    

    Lock 的所有者基于获取它的线程。这么多不同的调用者可以从不同的线程调用您的Resource 对象,并且锁会正常运行。

    如果您需要在 Resource 上调用多个方法,这需要您一直挂在锁上,这将无法正常工作。在这种情况下,您可能需要考虑使资源本身可锁定:

    class Resource():
        def __init__(self):
            self.lock = Lock()
    
        def __enter__(self):
            self.lock.acquire()
    
        def __exit__(self, *args, **kwargs):
            self.lock.release()
    
        def return_some_value(self):
            return self.somevalue
    
        def some_method(self):
            # stuff
    
        def some_other_method(self):
            # stuff
    
    r = Resource()
    with r:
        r.some_method()
        r.some_other_method()
        val = r.return_some_value()
    

    【讨论】:

    • 谢谢。我不确定在 return 语句之后是否会释放锁。但显然,with 语句(以及 try-finally)总是如此。我不知道。
    • @Peter Yep, as stated in the docs: "当在 try...finally 语句的 try 套件中执行 return、break 或 continue 语句时,finally 子句也会被执行'在出去的路上'"
    • 关于您回答的第二部分:如果这些方法彼此独立,那么两种变体都会同样有效,不是吗?
    • @Peter 我会说如果每个方法都是独立的,那么第一个选项会更好,因为客户端根本不需要担心锁定事情。如果属性/方法对所有线程都是只读的,则无需锁定它们。唯一的例外是只读方法正在使用某些无法共享的资源(例如,从文件对象读取的方法,或使用存储在实例中的迭代器)。尽管我想您可能会争辩说这并不是真正的“只读”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-21
    • 1970-01-01
    相关资源
    最近更新 更多