2011-08-06 6 views
3

私は属性 "observer"を持つpygletウィンドウを持っています。オブザーバーには辞書 "dict"があります。 main_loop()関数では、observer.dictの内容に従ってウィンドウが再描画されます。オブザーバ自体は、ストリームを読み込み、読み込みをdictに追加するスレッドです。オブザーバには、dictに古いアイテムがあるかどうかを確認するタイマースレッドもあります。タイマースレッドがある場合は、それらを削除します。Python:別のスレッドが辞書を変更している間に辞書を反復する

Windowsがdictを繰り返している間に項目が追加または削除されると、明らかに問題が発生する可能性があります。私の現在の回避策は、毎回ディクテーションの深いコピーを作成し、そのコピーを描画することです。それは動作するようですが、それは醜い解決策です。

私はPythonに特に新しく、特にw.r.tスレッド/ロックなどです。アイテムを追加または削除するときにオブザーバが「ロック」する必要があると思います。誰かが私のいくつかのヒントを最初に見てどこを与えることができますか?

私のコードの意味のある構造体を抽出しようとしましたが、すべてが長すぎます。私はあなたが主なアイデアを得ることができれば嬉しいです

class GraphConsole(window.Window): 
    def __init__(self, *args, **kwargs): 
     window.Window.__init__(self, *args, **kwargs) 

    def init(self, observer): 
     self.observer = observer 


    def main_loop(self): 
     while not self.has_exit: 
      ... 
      self.draw() 

    def draw(self): 
     dict_copy = deepcopy(self.observer.dict) # <-- UGLY WORKAROUND 
     for k, v in dict_copy.iteritems(): 
      ... 


class Observer(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 
     self.dict = {} 
     self.timer = Timer(1, self.delete_obsolete); 
     self.timer.start() 

    def run(self): 
     while True: 
     ... 
     # read a stream 
     self.dict.append(<new_element>) 
     ... 


    def delete_obsolete(self): 
     ... 
     del self.dict[...] 
     ... 



class Timer(threading.Thread): 
    def __init__(self, interval_in_seconds, func): 
     threading.Thread.__init__(self) 
     self.interval_in_seconds = interval_in_seconds 
     self.func = func 

    def run(self): 
     while True: 
     self.func(); 
     time.sleep(self.interval_in_seconds) 



if __name__ == "__main__": 

    observer = Observer(); 
    observer.start() 

    graph_console = GraphConsole() 
    graph_console.init(observer) 
    graph_console.main_loop() 

答えて

5

おそらく、いくつかの単純なロックで問題を解決できます。 Observerクラス:

class Observer(threading.Thread): 
    def __init__(self, lock): 
     threading.Thread.__init__(self) 
     self.dict_lock = lockthreading.RLock() 
     self.dict = {} 
     self.timer = Timer(1, self.delete_obsolete); 
     self.timer.start() 

    def run(self): 
     while True: 
     ... 
     with self._dict_lock: 
      # read a stream 
      self.dict.append(<new_element>) 
     ... 


    def delete_obsolete(self): 
     ... 
     with self._dict_lock: 
      del self.dict[...] 
     ... 

GraphConsoleクラス:

class GraphConsole(window.Window): 
    def __init__(self, *args, **kwargs): 
     window.Window.__init__(self, *args, **kwargs) 

    def init(self, observer): 
     self.observer = observer 

    def main_loop(self): 
     while not self.has_exit: 
      ... 
      self.draw() 

    def draw(self): 
     with self.observer.dict_lock: 
      for k, v in dict_copy.iteritems(): 
      ... 

私の最初の答えは少し不完全であったが、私はあなたの迅速な応答のためのアイデア:)

+0

おかげだ参照してください。私はそれがこのように見えるはずだと仮定していました。残念ながら、それはトリックをしません。もちろん、私の実際のコードに依存するかもしれません - 私はこれを見なければなりません。しかし、上記のabbrivatedコードは、構造をきちんとキャプチャする必要があります。 – Christian

+0

できるだけ少数の突然変異操作を行うことが役立つかもしれません。 dict(またはコピーを作成しようとしているもの)を削除する代わりに、フィルターを適用したバージョン(つまり**削除してはならないすべてのものを含む新しいdict - を作成してください。これをdeep-copyする必要はありませんBTW)、元のファイルとフィルタされたファイルを置き換えます。複数リーダまたはシングルライタのロックシステムでは、置換ステップのみをロックする必要があります。 (もちろん反復スレッドにもロックがあります) –

+0

私は自分のコードをリファクタリングしました:Observerはもはやスレッドではありませんが、Observer.dictにアイテムを追加/削除するObserverのメソッドを使って2つのスレッドに属性があります。 。コールバック関数(delete_obsoleteなど)とGraphConsole.drawメソッドの両方で、self.lockで「protext」を実行します。 (目に見える)効果なし。 - カール:私はあなたのアイデアを得ると思う、私はそれを調べるだろう。それでも、それはどういうわけかそのまま動作するはずです。とにかくありがとう! – Christian

関連する問題