2013-08-23 10 views
8

jakie jest prawidłowe rozwiązanie, aby mieć pewność, że plik nigdy nie zostanie uszkodzony podczas korzystania z wielu wątków i procesów.równoczesne zapisywanie do tego samego pliku przy użyciu wątków i procesów

wersja do gwintów, które dbają o błędy otwierania.

lock = threading.RLock() 
with lock: 
    try: 
    f = open(file, 'a') 
    try: 
     f.write('sth') 
    finally: 
     f.close() # try close in any circumstances if open passed 
    except: 
    pass # when open failed 

procesów Chyba musi używać multiprocessing.Lock

ale jeśli chcę 2 procesy, a pierwszy proces własne 2 Nitki (każdy plik jeden użytku)

jest tylko teoria, ale Chcę wiedzieć, jak połączyć synchronizację z wątkami i procesami. wątki "dziedziczą" go z procesu ?, więc wymagana jest tylko synchonizacja między procesami?

i 2. Nie jestem pewien, czy powyższy kod trzeba spróbować zagnieżdżony w przypadku, gdy nie powiedzie się pisać, a my chcemy blisko otwarty plik (co, jeśli to pozostanie otwarty po zamkiem zwolniony)

+0

Na marginesie, twoje 'try' /' finally' może (i zazwyczaj powinno) zostać zastąpione przez instrukcję 'with'. Ponadto, 'except 'pass' jest zwykle złym pomysłem - jeśli próbujesz przełknąć konkretny wyjątek, po prostu przełóż ten konkretnie, nie wszystko. Jeśli się tutaj rozejrzysz, znajdziesz dziesiątki pytań od ludzi, którzy mieli głupie błędy, jak przekazywanie int jako nazwy pliku, która nie pozwalała im na zauważanie i debugowanie. – abarnert

+0

Ponadto, jeśli blokujesz pliki specjalnie dla plików, możesz rozważyć zastosowanie blokad plików doradczych w POSIX i wyłącznego dostępu do plików w systemie Windows, zamiast ogólnych blokad wątków/procesów. – abarnert

+1

Inną możliwością jest wykonanie całego pliku dołączanego z jednego wątku (w jednym procesie), a wszyscy inni po prostu wysyłają wiadomości do kolejki (która nie wymaga żadnej synchronizacji, ponieważ jest wbudowana). – abarnert

Odpowiedz

7

Podczas tego ISN” Całkowicie zrozumiałe z the docs, prymitywy synchronizacji wieloprocesowej w rzeczywistości również synchronizują wątki.

Na przykład, jeśli uruchomić ten kod:

import multiprocessing 
import sys 
import threading 
import time 

lock = multiprocessing.Lock() 

def f(i): 
    with lock: 
     for _ in range(10): 
      sys.stderr.write(i) 
      time.sleep(1) 

t1 = threading.Thread(target=f, args=['1']) 
t2 = threading.Thread(target=f, args=['2']) 
t1.start() 
t2.start() 
t1.join() 
t2.join() 

... wyjście zawsze będzie 1111111111222222222 lub 22222222221111111111, nie mieszaniną dwóch.

Blokady są implementowane na wierzchu obiektów synchronizacji jądra systemu Win32 w systemie Windows, semaforów na platformach POSIX, które je obsługują, a nie zaimplementowane w ogóle na innych platformach. (Można to sprawdzić z import multiprocessing.semaphore, który zgłosi ImportError na innych platformach, jak wyjaśniono w docs.)


Powiedział, że jest to z pewnością bezpieczne mieć dwa poziomy zamków, tak długo, jak zawsze używasz ich we właściwej kolejności - to znaczy, nigdy nie chwytaj za threading.Lock, chyba że możesz zagwarantować, że Twój proces ma numer multiprocessing.Lock.

Jeśli zrobisz to wystarczająco sprytnie, może to przynieść korzyści. (Cross-process zamki na Windows, a na niektórych platformach POSIX, mogą być rzędy wielkości wolniejsze niż blokad wewnątrz procesowych.)

Jeśli po prostu zrobić to w sposób oczywisty (tylko zrobić with threadlock: wewnątrz with processlock: bloki), to oczywiście nie pomoże to w działaniu, a nawet spowolni trochę (choć prawdopodobnie nie wystarczy do zmierzenia) i nie przyniesie żadnych bezpośrednich korzyści. Oczywiście twoi czytelnicy będą wiedzieć, że twój kod jest poprawny, nawet jeśli nie wiedzą, że blokady między wątkami działają, a czasami debugowanie zakleszczeń intraprocesowych może być dużo łatwiejsze niż debugowanie zakleszczeń międzyprocesowych ... ale nie sądzę, że w większości przypadków jest to wystarczający powód do większej złożoności.