web-dev-qa-db-ja.com

Pythonファイルへの安全なマルチプロセッシング書き込み

私は多くのサブ問題を伴う大きな数値問題を解決しようとしています。そして、Pythonのマルチプロセッシングモジュール(特にPool.map)を使用して、異なる独立したサブ問題を異なるコアに分割しています。各サブ問題には多くのサブ問題が含まれます。これらの結果をプロセスによってまだ計算されていない場合はファイルに保存し、そうでない場合は計算をスキップしてファイルから結果を読み取ることで、これらの結果を効果的にメモしようとしています。

私はファイルの同時実行性の問題を抱えています:さまざまなプロセスが時々サブサブ問題がまだ計算されているかどうかを確認します(結果が保存されるファイルを探して)、そうでないことを確認し、計算を実行します、次に、同じファイルに同時に結果を書き込もうとします。このような衝突を記述しないようにするにはどうすればよいですか?

56
Big Dogg

@ 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 __== "__main__":
   main()
100
MikeHunter

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 __== '__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')
0
fizix137