Python多处理安全地写入文件


76

我正在尝试解决一个涉及许多子问题的大数值问题,并且我正在使用Python的多处理模块(特别是Pool.map)将不同的独立子问题分解为不同的核心。每个子问题都涉及计算大量子问题,并且我试图通过将结果存储到文件中(如果尚未通过任何过程对其进行计算)来有效地记住这些结果,否则请跳过计算并仅从文件中读取结果。

我的文件存在并发问题:有时,不同的过程会检查以查看是否已经计算了子子问题(通过查找将结果存储在文件中),没有找到子子问题,运行计算,然后尝试同时将结果写入同一文件。如何避免这样写冲突?


3
从文档中查看一个multiprocessing.Lock用于同步多个进程的示例。
约翰·文亚德

13
您可能只有一个进程来写入结果,而Queue作为输入可以由其他工作进程提供。我认为将所有工作进程设为只读是安全的。
GP89

我应该提到,为了使事情变得更复杂,我在集群上同时运行多个不同的主要主要问题,每个问题都将结果写入同一网络文件系统上的子子问题。因此,我可以从完全在单独的计算机上运行的进程中获得冲突(因此,我认为使用multiprocessing.Lock之类的解决方案不起作用)。
Big Dogg

2
如果您的网络文件系统支持文件锁定,则可以使用os特定的文件创建方法来独占创建文件,并对它持有独占锁,直到结果准备就绪,然后将其关闭。任何未能“赢得”创建竞赛的进程都将尝试将其打开并重试(延迟),直到能够将其打开,然后他们才能读取结果。
JimP 2012年

10
您实际上是在这里对数据库服务器进行编程。您是否考虑过使用现有的?
乔治,2012年

Answers:


137

@ GP89提到了一个很好的解决方案。使用队列将写入任务发送到对文件具有唯一写访问权​​的专用进程。其他所有工人均具有只读访问权限。这将消除冲突。这是一个使用apply_async的示例,但它也适用于map:

import multiprocessing as mp
import time

fn = 'c:/temp/temp.txt'

def worker(arg, q):
    '''stupidly simulates long running process'''
    start = time.clock()
    s = 'this is a test'
    txt = s
    for i in range(200000):
        txt += s 
    done = time.clock() - start
    with open(fn, 'rb') as f:
        size = len(f.read())
    res = 'Process' + str(arg), str(size), done
    q.put(res)
    return res

def listener(q):
    '''listens for messages on the q, writes to file. '''

    with open(fn, 'w') as f:
        while 1:
            m = q.get()
            if m == 'kill':
                f.write('killed')
                break
            f.write(str(m) + '\n')
            f.flush()

def main():
    #must use Manager queue here, or will not work
    manager = mp.Manager()
    q = manager.Queue()    
    pool = mp.Pool(mp.cpu_count() + 2)

    #put listener to work first
    watcher = pool.apply_async(listener, (q,))

    #fire off workers
    jobs = []
    for i in range(80):
        job = pool.apply_async(worker, (i, q))
        jobs.append(job)

    # collect results from the workers through the pool result queue
    for job in jobs: 
        job.get()

    #now we are done, kill the listener
    q.put('kill')
    pool.close()
    pool.join()

if __name__ == "__main__":
   main()

1
嘿,迈克,谢谢您的回答。我认为这可以解决我所说的问题,但是我不确定它是否可以解决问题注释中概述的全部问题,特别是我如何在联网的多台计算机上运行多个主程序文件系统,所有这些文件系统可能都具有尝试写入同一文件的进程。(FWIW,我前段时间以骇人听闻的方式解决了我的个人问题,但在别人有类似问题的情况下发表评论。)
Big Dogg 2012年

4
我真的很想多次投票。这对我很有帮助。今天再来一次。
爱德华多

12
我必须在pool.join()下面添加一个pool.close()。否则,我的工作人员将在收听者之前完成,过程将停止。
herrherr 2014年

2
当使用者的数量大大超过数量并导致内存问题时该怎么办?您将如何实现多个使用者都写入同一文件?
ccdpowell'3

15
为什么mp.cpu_count() + 2设置进程数?
JenkinsY

1

在我看来,您需要用来Manager将结果临时保存到列表中,然后将列表中的结果写到文件中。另外,用于starmap传递要处理的对象和托管列表。第一步是构建要传递给的参数starmap,其中包括托管列表。

from multiprocessing import Manager
from multiprocessing import Pool  
import pandas as pd

def worker(row, param):
    # do something here and then append it to row
    x = param**2
    row.append(x)

if __name__ == '__main__':
    pool_parameter = [] # list of objects to process
    with Manager() as mgr:
        row = mgr.list([])

        # build list of parameters to send to starmap
        for param in pool_parameter:
            params.append([row,param])

        with Pool() as p:
            p.starmap(worker, params)

从这一点上,您需要确定如何处理列表。如果您有大量的RAM和庞大的数据集,请使用熊猫进行串联。然后,您可以非常轻松地将文件另存为csv或pickle。

        df = pd.concat(row, ignore_index=True)

        df.to_pickle('data.pickle')
        df.to_csv('data.csv')

2
我可以得到一些有关为什么拒绝投票的反馈吗?我看到公认的答案更好。我只想学习。
fizix137
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.