2017-08-25 341 views
1

我已经创建了带有多线程的python脚本,每个线程都向全局dict写入值,这是线程安全的,因为每个线程都使用新的唯一值更新字典,我不希望每个线程都保存在输出文件中的字典的结果,但我收到“字典在迭代过程中改变大小”,有没有办法做到这一点,如锁定字典在转储到文件时写入,我试图锁定和释放,但没有工作python多线程保存字典结果

def do_function(): 
    while True: 
     r=q.get() 
     global_dict[r]={} --> this is thread safe as r is unique it will not repeat again 
     telephone,address=get_info(r) 
     global_dict[r]['t']=telephone 
     global_dict[r]['a']=address 

     with open("output.pickle","wb") as j: --> save to file 
       pickle.dump(global_dict,j) --> receive error dictionary changed size during iteration 

     q.task_done() 

global dict={} 
thread=10 
q = Queue(threads * 2) 
for i in range(concurrent): 
    t = Thread(target=do_function) 
    t.daemon = True 
    t.start() 
for p in lst: 
     q.put(p) 
    q.join() 
+0

您提供的两行代码是无效的Python,并且它们也不会形成[mcve]。告诉我们什么没有工作。 –

+0

重复? https://stackoverflow.com/questions/1312331/using-a-global-dictionary-with-threads-in-python – Alexander

+0

不重复,我已经看到了这一点,它谈到哪些操作在字典中是线程安全的,哪一个你应该锁定并释放 – Amr

回答

0

您不需要在线程中执行写入字典。也许这是一个错误。 因为这是一个全球字典。您可以在所有线程完成后执行此操作,只需将

with open("output.pickle","wb") as j: 
    pickle.dump(global_dict,j) 

移动到文件末尾。

你的错误是由当一个线程被倾销到文件的字典引起的,而另一个线程改变了字典,所以在迭代

编辑了1

第一个线程会抱怨字典改变大小

我认为简单的解决办法是不要使用全局变量,那么错误不会发生。 这样的:

import threading 
lock = threading.Lock() 

def do_function(): 
    while True: 
     r=q.get() 
     d={} 
     telephone,address=get_info(r) 
     d['t']=telephone 
     d['a']=address 
     lock.acquire() 
     with open("output.pickle","ab") as j: 
       pickle.dump(d,j) 
     lock.release() 
     q.task_done() 

,并注意使用“AB”模式打开文件进行追加不更换,不使用“WB”。

组织编写

使用锁定每次写入文件可能有沉重的代价。解决方法是将每个线程写入到不同的文件中,该文件可以通过进入该线程时生成的uuid命名。

而更快的方法是当写时,您可以进行批量写入和使用锁定。它会比老年人更快。

示例代码:

import threading 
lock = threading.Lock() 

def do_function(): 
    buffer = [] 
    while True: 
     r=q.get() 
     d={} 
     telephone,address=get_info(r) 
     d['t']=telephone 
     d['a']=address 
     buffer.append(d) 
     q.task_done() 

     if len(buffer) >= BATCH_COUNT: 
      lock.acquire() 
      with open("output.pickle","ab") as j: 
        pickle.dump(buffer,j) 
      lock.release() 
      buffer = [] 

的BATCH_COUNT可能是1000或10000,或者你喜欢的东西。

+0

是的,我知道,我想不断地将它写入文件,而不是在所有线程完成之后,如果程序崩溃了,它不需要从头开始重复,它会从它离开的地方继续,是有办法做到这一点? – Amr

+0

答案更新了,希望能帮到你 – GuangshengZuo

+0

所有的线程同时写入一个文件,我认为这会使文件不可读,内容会重叠,或者写入文件是线程安全的? – Amr