Python: 在一个线程修改字典时遍历字典

3 投票
1 回答
1564 浏览
提问于 2025-04-16 23:02

我有一个 pyglet 窗口,它有一个叫做 "observer" 的属性。这个 observer 有一个字典 "dict"。在 main_loop() 函数中,窗口会根据 observer.dict 的内容重新绘制窗口。这个 observer 本身是一个线程,它在读取数据流,并把读取到的数据添加到 dict 中。observer 还有一个定时线程,每秒检查一次 dict 中是否有过时的项目,如果有,就把它们删除。

显然,如果在窗口遍历 dict 的时候,有项目被添加或删除,这可能会引发问题。我现在的解决办法是每次都对 dict 进行深拷贝,然后绘制这个拷贝。虽然这样似乎能解决问题,但我觉得这个方法不太优雅。

我对 Python 还很陌生,尤其是关于线程、锁等方面。我想我需要让 observer 在添加或删除项目时“锁定” dict。有没有人能给我一些建议,让我知道该从哪里开始?

我试着提炼出我代码的结构,但所有内容都太长了。我希望你能理解我的主要想法。

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()

1 个回答

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[...]
         ...

图形控制台类:

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():
           ...

我最开始的回答有点不完整,不过我看到你明白了这个思路 :)

撰写回答