我正在尝试创建一个可以从另一个线程注册到对象的简单回调。在这种情况下,调用回调的初始对象在其自己的线程上运行。
通过以下示例可以最好地说明这一点:
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和代码对象的支持。
我可能根本就没有读那么正确。我是否需要添加锁定,或者一切都很好而且线程安全吗?
非常感谢
答案 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
块来测试它。