Python багатопроцесорний безпечний запис у файл


80

Я намагаюся вирішити велику числову проблему, яка включає безліч підпроблем, і я використовую багатопроцесорний модуль Python (зокрема Pool.map), щоб розділити різні незалежні підзадачі на різні ядра. Кожна підзадача включає обчислення безлічі підпроблем, і я намагаюся ефективно запам'ятати ці результати, зберігаючи їх у файлі, якщо вони ще не були обчислені в будь-якому процесі, інакше пропустимо обчислення і просто прочитаємо результати з файлу.

У мене проблеми з паралельністю з файлами: різні процеси іноді перевіряють, чи не обчислюється ще підпроблема (шукаючи файл, де будуть зберігатися результати), переконайтеся, що цього не було, запустіть обчислення, потім спробуйте записати результати в той самий файл одночасно. Як мені уникнути подібних зіткнень?


3
Перегляньте приклад із документації використання multiprocessing.Lockдля синхронізації декількох процесів.
Джон Він'ярд

13
Ви можете мати лише один процес запису результатів, із чергою як вхідними даними, яку можуть подавати інші робочі процеси. Я вважаю, що було б безпечно мати всі робочі процеси лише для читання.
GP89

Я повинен був згадати, що, щоб ускладнити ситуацію, я запускаю кілька різних великих основних проблем одночасно на кластері, причому кожна з них записує результати в підпроблеми в одній мережевій файловій системі. Таким чином, я можу отримати зіткнення з процесів, що працюють на окремих машинах (тому я не думаю, що рішення, що використовують такі речі, як багатопроцесорність. Lock будуть працювати).
Big Dogg

2
Якщо ваша мережева файлова система підтримує блокування файлів, ви можете використовувати метод створення конкретного файлу os, щоб виключно створити файл і утримувати на ньому ексклюзивний замок, поки результати не будуть готові, а потім закрийте його. Будь-який процес, який не зміг "перемогти" у створеній гонці, спробував би її відкрити та повторити спробу (із затримкою), поки не змогли її відкрити, тоді вони зможуть прочитати результати.
JimP

10
Ви по суті тут програмуєте сервер баз даних. Ви роздумували про використання вже існуючого?
georg

Відповіді:


139

@ GP89 згадав хороше рішення. Використовуйте чергу, щоб надіслати завдання написання до спеціального процесу, який має єдиний доступ до запису у файл. Усі інші працівники мають доступ лише для читання. Це усуне зіткнення. Ось приклад, який використовує apply_async, але він також буде працювати з картою:

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,

4
Я справді хотів би висловитись за це багато разів. Це було мені корисно стільки разів. Сьогодні ще раз.
Едуардо

12
Мені довелося додати pool.join()нижче pool.close(). Інакше мої працівники закінчували б перед слухачем, і процес просто зупинявся.
herrherr

2
Що можна сказати про те, коли споживач значно перевершує чисельність і викликає проблеми з пам’яттю? Як би ви реалізували декількох споживачів, які всі записують в один файл?
ccdpowell

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)

З цього моменту вам потрібно вирішити, як ви збираєтеся обробляти список. Якщо у вас тонни оперативної пам’яті та величезний набір даних, сміливо об’єднуйтесь за допомогою панд. Тоді ви можете дуже легко зберегти файл у форматі CSV або засолу.

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

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

3
Чи можу я отримати відгук про те, чому за це було проголосовано? Я бачу, що прийнята відповідь набагато краща. Я просто хочу вчитися.
fizix137
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.