2011-08-06 106 views
3

我有一个pyglet窗口,它具有属性“observer”。观察员有字典“字典”。在main_loop()函数中,窗口根据observer.dict的内容重新绘制窗口。观察者本身是一个读取流的线程,将读数添加到字典中。观察者还有一个计时器线程,用于检查每一秒是否在dict中有过时的项目,如果是,则删除它们。Python:迭代字典,而另一个线程修改字典

显然,如果在窗口迭代字典时添加或删除项目,可能会导致问题。我目前的解决方法是让每次都是字典的深层副本,并绘制副本。它似乎工作,但它是一个丑陋的解决方案。

我对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

大概一些简单的锁可以解决你的问题。观察类:

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

感谢您的快速回复。我认为它应该看起来像这样。不幸的是,它并没有做到这一点。这当然取决于我的实际代码 - 我必须看看这个。但是,上面的补充代码应该很好地捕捉结构。 – Christian

+0

你可能会发现尽可能少的变异操作会有帮助。不要从字典中删除东西(或试图制作副本),请创建一个过滤版本(即包含**不应该被删除的所有内容的新字典 - 而且您不需要深拷贝此内容顺便说一句),然后用过滤版本替换原件。鉴于多读取器或单写入器锁定系统,只有更换步骤需要锁定。 (当然在迭代线程中也有一个锁) –

+0

我重构了我的代码:观察者不再是一个线程,但有2个线程具有通过回调调用Observer方法向/从Observer.dict添加/删除项目的属性。在两个回调函数(例如delete_obsolete)以及GraphConsole.draw方法中,我通过“with self.lock”通过“protext”。没有(可见)效果。 - @Karl:我想我已经明白你的想法了,我会研究一下。不过,我认为它应该以某种方式正常工作。不管怎么说,还是要谢谢你! – Christian