2013-01-24 31 views
7

我有一個文件名數組,每個進程只需要創建和寫入一個文件。以線程安全的方式創建文件

這就是我來:

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 
} 

但我不喜歡這樣,我依靠filesize

任何建議以另一種(更好)的方式做到這一點?

UPD:添加標籤,輕鬆地討論

UPD 2:我使用filesize,因爲我沒有看到任何其他可靠的方法來檢查,如果當前線程創建的文件(因此,它是空的)

UPD 3:該解決方案應該條件種族免費。

+0

你試圖通過這樣做解決什麼問題? – cgTag

+0

@Mathew Foscarini:它管理沒有併發同步的第三方資源。 – zerkms

回答

3

一種可能,稍微醜陋的解決方案將鎖定一個鎖文件,然後測試文件是否存在:

$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); 
} 

這應該允許獨佔訪問該文件,並且還允許決定是否調用fopen($VMidFile, 'c');將創建該文件。

+0

是的,這可以工作(同步文件的同步文件 - 「我們需要更深入」);-) +1 – zerkms

+0

但不能泄漏鎖定,如果它在臨界區崩潰oO – Dmitry

2

而不是創建一個文件,並希望它不干擾:

  1. 創建臨時文件
  2. 做就可以
  3. rename到新的位置,如果位置的所有必要的文件操作不存在。

從技術上講,由於rename將覆蓋目標,所以併發線程仍然有可能發生衝突。這是非常不可能的,如果您有:

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

你可以使用md5_file驗證文件內容,該塊之後是正確的。

+0

「如果位置不存在,則將其重命名爲新位置。」 ---你如何以線程安全的方式檢查它? 「有機會」 - 我不想依靠「機會」。 「這是不太可能的」---我不想依賴「可能與否」,我希望能夠保證**的解決方案始終按預期工作。 – zerkms

+0

那麼你有沒有條件競爭的提案?現在這不是一個答案,對不起。 – zerkms

+0

我喜歡這個建議;它使關鍵部分很小。 – Dmitry

1

可以保證使用semaphores獨佔訪問(僅適用於UNIX,並且所提供的sysvsem擴展安裝):

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

// Do some critical work... 

sem_release($s); 

否則,你也可以使用flock。它不需要任何特殊的擴展,但根據comments on PHP.net比使用信號燈慢一點:

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

// Critical stuff, again 

flock($a, LOCK_UN); 
+0

如果你檢查我的問題,你會看到,我已經在使用'flock' – zerkms

+0

Ouch,你是對的...... :)那麼使用信號燈怎麼樣? – helmbert

+0

他們將導致相同的問題 - 如何檢查文件是否由當前進程創建。我不知道如何使用信號量來同步'file_exists' – zerkms

0

在fopen調用中使用模式'x'而不是'c'。並檢查生成的$ fp,如果它爲假,該文件不是由當前線程創建的,並且應該繼續到下一個文件名。如果fopen($ VMidFile,'x')無法創建該文件,因爲它已經存在,您可能希望在fopen調用前放置一個@以禁止任何警告。

這應該工作,即使沒有羊羣。

+0

如果腳本死亡並且不清除文件會怎麼樣?這需要人工交互來清理文件以再次運行,不是嗎? – zerkms

+0

不知道這是如何涉及到原始問題,你是什麼意思「清理文件」?之後刪除它,或..?只要您可以定義確定另一個線程是否仍在運行並處理該文件的確切條件,或者它是否崩潰以及文件是否已成爲孤立文件,那麼它可以自動處理。 – Rogier

+0

所以這就是最初的問題:「只要你可以定義確定另一個線程是否仍在運行的確切條件」---這就是我問到的鎖定機制。該機制應該是可靠的,以便它不需要任何額外的啓發式。根據您的提議,我發現如果某個進程死亡並且不會刪除該文件,算法將會卡住。 – zerkms