字典在多线程应用程序的迭代期间改变了大小

时间:2017-07-24 09:04:39

标签: python multithreading dictionary

我不知道如何解决这个问题:

  

Traceback(最近一次调用最后一次):文件   " /usr/local/cabinet_dev/cabinet/lib/python3.4/site-packages/eventlet/hubs/hub.py" ;,   第458行,在fire_timers中       timer()File" /usr/local/cabinet_dev/cabinet/lib/python3.4/site-packages/eventlet/hubs/timer.py",   第58行,致电       cb(* args,** kw)File" /usr/local/cabinet_dev/cabinet/lib/python3.4/site-packages/eventlet/greenthread.py",   第218行,主要       result = function(* args,** kwargs)文件" ./ monitor.py",第148行,在caughtBridge中       对于self.active.keys()中的调用:RuntimeError:迭代期间字典改变了大小

在下面的代码中:

def caughtBridge(self):
    while True:
        event = self.bridgeQueue.get()
        uniqueid1 = str(event.headers.get('Uniqueid1'))
        uniqueid2 = str(event.headers.get('Uniqueid2'))
        for call in self.active.keys():
            if self.active[call]['uniqueid'] == uniqueid1:
                self.active[call]['uniqueid2'] = uniqueid2
            if self.active[call]['uniqueid'] == uniqueid1:
                for listener in self.listeners:
                    for number in listener.getNumbers():
                        if number == self.active[call]['exten']:
                            if not self.active[call]['answered']:
                                self.sendEvent({"status": "bridge", "id": self.active[call]['uniqueid'],
                                                "number": self.active[call]['exten']},
                                               listener.getRoom())
                                self.__callInfo(self.active[call], listener.getRoom())
                                self.active[call]['answered'] = True
        self.bridgeQueue.task_done()

1 个答案:

答案 0 :(得分:2)

使用self.active.keys()的副本,例如:

for call in list(self.active.keys()):

未检测到添加删除 dict条目?
如果添加,其他线程将看不到添加的dict条目。
如果删除,当前线程将因 键错误 而失败, 你必须抓住这些。

例如:

for call in list(self.active.keys()):
    <Lock that call to prevent removing>
    if call in self.active:
        ...
                      self.active[call]['answered'] = True
    else:
        # call removed do nothing
    <Unlocked that call to do whatever in other Thread>
self.bridgeQueue.task_done()

阅读Python»3.6.2文档:threading.html#lock-objects

基本实现配对方法self.lock(call)self.unlock(call),例如:

  

未经测试的代码
  要防止死锁,您必须保证会self.unlock(call)到达!

class xxx
    def __init__....
        self_lock = threading.Lock
        # Init all  self.active[call]['lock'] = False

def lock(self, call):
    # self._lock ist class threading.Lock
    # self._lock has to be the same for all Threads
    with self._lock:
        if call in self.active and not self.active[call]['lock']:
            self.active[call]['lock'] = True
            return True
        else:
            return False

def unlock(self, call):
    with self._lock:
        self.active[call]['lock'] = False

# Usage:
for call in list(self.active.keys()):
    if self.lock(call):
        ...
                      self.active[call]['answered'] = True

        self.unlock(call)
    else:
        # call removed do nothing
self.bridgeQueue.task_done()