2011-01-26 113 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); 
}