Python3 weakref WeakMethod和线程安全

时间:2017-12-12 08:45:02

标签: python multithreading

我正在尝试创建一个可以从另一个线程注册到对象的简单回调。在这种情况下,调用回调的初始对象在其自己的线程上运行。

通过以下示例可以最好地说明这一点:

from pprint import pprint
import sys
import weakref
import threading
import time

class DummyController(object):

  def __init__(self):
    self.name = "fortytwo"

  def callback(self):
    print("I am number : " + self.name)

class SomeThread(threading.Thread):

  def __init__(self, listener):
    threading.Thread.__init__(self)
    self.listener = listener

  def run(self):
    time.sleep(1)
    dummy = DummyController()
    self.listener.register_callback(dummy.callback)
    time.sleep(5)
    del dummy



class Listener(threading.Thread):

  def __init__(self):
    threading.Thread.__init__(self)
    self.runner = weakref.WeakMethod(self.default_callback)
    self.counter = 20

  def default_callback(self):
    print("Not implemented")

  def register_callback(self, function):
    self.runner = weakref.WeakMethod(function)

  def run(self):
    while self.counter:
      try:
        self.runner()()
      except Exception as e:
        pprint(e)

      self.counter -= 1
      time.sleep(1)



listen = Listener()
some = SomeThread(listen)

listen.start()
some.start()

现在上面的代码工作得很好。但我担心这里的线程安全。通过weakref docs阅读,如果weakref真的是线程安全的,那么它是否非常清楚,除了这行:

  

在3.2版中更改:添加了对thread.lock,threading.Lock和代码对象的支持。

我可能根本就没有读那么正确。我是否需要添加锁定,或者一切都很好而且线程安全吗?

非常感谢

1 个答案:

答案 0 :(得分:0)

好的,我明白了。这不是关于线程安全的问题,而只是关于weak reference的问题。

有一个可执行的例子:

from pprint import pprint
import sys
import weakref
import threading
import time
import gc

class SomeThread(threading.Thread):

    def __init__(self, listener):
        threading.Thread.__init__(self)
        self.listener = listener

    def run(self):
        class test:    # simplify this example.
            def callback(self, count):
                print(count)
        time.sleep(1)
        dummy = test()
        self.listener.register_callback(dummy.callback)
        time.sleep(5)
        del dummy
        gc.collect()  # add this line to do garbage collecting.


class Listener(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.runner = weakref.WeakMethod(self.default_callback)
        self.counter = 20

    def default_callback(self):
        print("Not implemented")

    def register_callback(self, function):
        self.runner = weakref.WeakMethod(function)

    def run(self):
        while self.counter:
            try:
                self.runner()(self.counter)
            except Exception as e:
                pprint(e)

            self.counter -= 1
            time.sleep(1)


listen = Listener()
some = SomeThread(listen)

listen.start()
some.start()

输出:

TypeError('default_callback() takes 1 positional argument but 2 were given',)
TypeError('default_callback() takes 1 positional argument but 2 were given',)
18
17
16
15
TypeError("'NoneType' object is not callable",)
TypeError("'NoneType' object is not callable",)
TypeError("'NoneType' object is not callable",)

如果您明确致电gc.collect()callback会丢失其最后一个强引用,然后它会变为None。因为你永远不知道什么时候会gc收集垃圾,所以存在一个潜在的问题。

无论你是否使用线程,只是weak reference的正常行为。

顺便说一句,请注意退出SomeThread.run也会隐式del dummy,您可以删除del dummy并将gc.collect()移动到try块来测试它。