2011-01-26 98 views
10

如閒置的好奇心比什麼都重要的鍛鍊,可以考慮下面這個簡單的日誌類:.NET 2.0:File.AppendAllText(...) - 線程安全的實現

internal static class Logging 
{ 
    private static object threadlock; 

    static Logging() 
    { 
     threadlock = new object(); 
    } 

    internal static void WriteLog(string message) 
    { 
     try 
     { 
      lock (threadlock) 
      { 
       File.AppendAllText(@"C:\logfile.log", message); 
      } 
     } 
     catch 
     { 
      ...handle logging errors... 
     } 
    } 
} 

圍繞File.AppendAllText(...)lock需要或者該方法是否由於其自身的實現而具有固有的線程安全性?

尋找這方面的信息會產生很多矛盾的信息,有些人說是,有些人說不。 MSDN什麼也沒說。

回答

16

File.AppendAllText將在日誌文件上獲得獨佔寫入鎖定,這將導致任何併發線程嘗試訪問該文件以引發異常。所以是的,你需要一個靜態鎖定對象來防止多個線程同時嘗試寫入日誌文件並引發一個IOException

如果這將是一個問題,我真的建議記錄到數據庫表,這將更好地處理併發日誌編寫器。

或者,您可以使用TextWriterTraceListener這是線程安全的(好吧,它會爲您鎖定;我寧願寫儘可能少的自己的多線程代碼)。

+1

這是矛盾的。如果File.AppendAllText有一個獨佔的寫鎖,爲什麼你需要單獨的鎖? – iheanyi 2014-02-26 21:42:43

+4

你誤會了,它是被鎖定寫入的文件。所以任何其他試圖同時寫入的線程都會收到異常。爲了防止這種情況發生,您必須使用程序中的鎖來序列化寫入。 – Pradeep 2014-07-03 20:05:44

0

它是線程安全的,因爲它使用讀共享來打開文件,因此假定您的文件系統支持文件鎖定,則一次只允許一個線程寫入文件。但是,如果其他線程嘗試讀取同一文件,則可能會得到髒讀。

0

測試並行寫入表明,如果要註釋掉您的鎖定語句,您將得到一個System.IO.IOException。

[Test] 
public void Answer_Question() 
{ 
    var ex = Assert.Throws<AggregateException>(() => Parallel.Invoke(
     () => Logging.WriteLog("abc"), 
     () => Logging.WriteLog("123") 
    )); 

    // System.IO.IOException: The process cannot access the file 'C:\Logs\thread-safety-test.txt' because it is being used by another process. 
    Console.Write(ex); 
}