2017-12-12 3 views
0

私は別のスレッドからオブジェクトに登録できる単純なコールバックを作成しようとしています。この場合、コールバックを呼び出す初期オブジェクトは独自のスレッドで実行されています。Python3 weakref WeakMethodとスレッドセーフ

これは最高次の例を通して説明されています

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が本当にラインを除いて、安全かいないスレッドである場合には非常に明確ではありません:thread.lock、threading.Lock、およびコードオブジェクトのサポートが追加されました:

は、バージョン3.2で変更します。

私は単にその権利を読んでいないかもしれません。私は、ロックを追加する必要がありますか、またはすべての実際に細かい、かなりスレッドセーフです?

ありがとうございました

+0

docはバージョン3.2で、thread.lock、threading.Lock、およびcodeオブジェクトを弱参照できますが、内部ロックがあることを意味するわけではありません。そして私はどの部分が安全でないのか分かりません。あなたは説明するための例を挙げることができますか? – Sraw

+0

'Listener'はオブジェクト関数のコールバックを実行しますが、別のスレッド(' SomeThread')がオブジェクトを削除するという事実から心配しています。 'weakref.WeakMethod'を使用しているので、オブジェクトはガベージコレクタの関与なしにメモリから即座に削除されるはずです。オブジェクトが削除されている場合にコールバック関数オブジェクトを削除すると競合状態が発生するかどうかと、同時に呼び出される。 これは少し明確ですか? – karmalis

答えて

0

OK、わかりました。これはスレッドセーフの問題ではなく、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を削除し、tryブロックにgc.collect()を移動することによってそれをテストすることができますように注意してください。

+0

ええ、コールバックが最後の強い参照を「必要とする」必要があるため、wakrefを使用している理由の1つです。したがって例外キャプチャ、しかし、私は、強い参照が別のスレッドで破壊されるようなことが競合状態を作り出すという事実を心配していました。 答えに感謝します。 – karmalis

関連する問題