2013-01-24 23 views
7

Mam tablicę nazw plików, a każdy proces musi tworzyć i zapisywać tylko do jednego pliku.Utwórz plik w sposób bezpieczny dla wątków

To właśnie przyszedł do:

foreach ($filenames as $VMidFile) { 
    if (file_exists($VMidFile)) { // A 
     continue; 
    } 

    $fp = fopen($VMidFile, 'c'); // B 

    if (!flock($fp, LOCK_EX | LOCK_NB)) { // C 
     continue; 
    } 

    if (!filesize($VMidFile)) { // D 
     // write to the file; 

     flock($fp, LOCK_UN); 
     fclose($fp); 
     break; 
    } 

    flock($fp, LOCK_UN); 
    fclose($fp); // E 
} 

Ale nie podoba mi się, że jestem powołując się na filesize.

Jakieś propozycje, aby zrobić to w inny (lepszy) sposób?

UPD: dodano etykiety łatwo dyskutować

UPD 2: Używam filesize bo nie widzę innego niezawodny sposób, aby sprawdzić, czy prąd wątek utworzony plik (tak więc jest to jeszcze pusty)

UPD 3: rozwiązanie powinny być wyścigu darmo.

+0

jaki problem próbujesz rozwiązać, wykonując te czynności? – cgTag

+0

@Mathew Foscarini: zarządza zasobami innych firm, które nie mają synchronizacji współbieżności. – zerkms

Odpowiedz

3

Możliwe, nieco brzydki rozwiązaniem byłoby, aby zablokować na plik blokady, a następnie testowanie jeśli plik istnieje:

$lock = fopen("/tmp/".$filename."LOCK", "w"); // A 

if (!flock($lock, LOCK_EX)) { // B 
    continue; 
} 
if(!file_exists($filename)){ // C 
    //File doesn't exist so we know that this thread will create it 
    //Do stuff to $filename 
    flock($lock, LOCK_UN); // D 
    fclose($lock); 
}else{ 
    //File exists. This thread didn't create it (at least in this iteration). 
    flock($lock, LOCK_UN); 
    fclose($lock); 
} 

Powinno to pozwolić na wyłączny dostęp do pliku, a także umożliwia podjęcie decyzji, czy połączenie do fopen($VMidFile, 'c'); utworzy plik.

+0

Tak, to może działać (pliki synchronizacji dla plików synchronizacji - "musimy iść głębiej") ;-) +1 – zerkms

+0

, ale nie może to przeciekać blokady, jeśli zawiesza się w sekcji krytycznej oO – Dmitry

2

Zamiast tworzenia pliku i mając nadzieję, że to nie jest zakłócana:

  1. utworzyć plik tymczasowy
  2. zrobić wszystkie operacje niezbędne do plików na nim
  3. rename go do nowej lokalizacji, jeżeli lokalizacja nie istnieje.

Technicznie, ponieważ rename nadpisze miejsce docelowe, istnieje szansa, że ​​współdziałające wątki będą nadal kolidować. To bardzo mało prawdopodobne, jeśli masz:

if(!file_exists($lcoation) { rename(... 

Można użyć md5_file aby zweryfikować zawartość pliku jest prawidłowa po tym bloku.

+0

"zmień nazwę na nową, jeśli lokalizacja nie istnieje." --- jak można to sprawdzić w sposób bezpieczny dla wątków? "jest szansa" - nie chcę polegać na "przypadku". "To bardzo mało prawdopodobne" --- nie chcę polegać na "prawdopodobnie lub nie", chcę rozwiązania, które ** gwarantuje **, że zawsze będzie działało zgodnie z oczekiwaniami. – zerkms

+0

Masz więc propozycję wyścigu, który nie jest warunkiem? Teraz to nie jest odpowiedź, przepraszam. – zerkms

+0

Podoba mi się ta propozycja; sprawia, że ​​sekcja krytyczna jest bardzo mała. – Dmitry

1

można zabezpieczyć przy użyciu semaphores wyłącznego dostępu (tylko Unix i pod warunkiem rozszerzenia sysvsem jest zainstalowany):

$s = sem_get(ftok($filename), 'foo'); 
sem_acquire($s); 

// Do some critical work... 

sem_release($s); 

przeciwnym razie można również użyć flock. To nie wymaga żadnych specjalnych rozszerzeń, ale według comments on PHP.net jest nieco wolniejsze niż przy użyciu semaforów:

$a = fopen($file, 'w'); 
flock($a, LOCK_EX); 

// Critical stuff, again 

flock($a, LOCK_UN); 
+0

Jeśli sprawdzisz moje pytanie, zobaczysz, że już używam 'flock'a – zerkms

+0

Ouch, masz rację ... :) A co z używaniem semaforów? – helmbert

+0

będą prowadzić do tego samego pytania - jak sprawdzić, czy plik został utworzony przez bieżący proces. Nie jestem pewien, czy rozumiem, jak mogę zsynchronizować 'file_exists' z semaforem – zerkms

0

Użyj trybu "x" zamiast "c" w swoim wywołaniu fopen. I sprawdź wynikową wartość $ fp, jeśli jest to fałsz, plik nie został utworzony przez bieżący wątek i powinieneś przejść do następnej nazwy pliku.

Ponadto, w zależności od ustawień instalacji PHP, możesz umieścić znak @ przed wywołaniem fopen, aby pominąć wszelkie ostrzeżenia, jeśli fopen ($ VMidFile, 'x') nie może utworzyć pliku, ponieważ już istniał.

To powinno działać nawet bez stada.

+0

Co się stanie, jeśli skrypt zginie i nie wyczyści pliku? Wymagałoby interakcji z człowiekiem, aby wyczyścić plik, aby działał ponownie, prawda? – zerkms

+0

Nie wiesz, jak to się ma do pierwotnego problemu, co masz na myśli mówiąc "wyczyść plik"? Usunąć go później, czy ...? Można to zrobić automatycznie, o ile możesz określić dokładne warunki, które decydują o tym, czy inny wątek nadal działa i pracuje nad plikiem, czy też się zawiesił, a plik został osierocony. – Rogier

+0

, więc jest to oryginalne pytanie: "o ile możesz zdefiniować dokładne warunki, które decydują o tym, czy inny wątek wciąż działa" --- o to pytałem o mechanizm blokujący. Mechanizm powinien być niezawodny, aby nie wymagał żadnej dodatkowej heurystyki. Z twoją propozycją widzę, że algorytm utknie, jeśli proces zginie i nie usunie pliku. – zerkms

Powiązane problemy