如何使静态类线程安全?

7

我有一个简单的静态日志类,但它绝对不是线程安全的,因为每个调用都试图写入同一个文件。我会得到这些异常:

The process cannot access the file 'logfile.txt' because it is being used by another process.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 
...

什么是最好的使其线程安全的方法?
public static class Logger
{
    private static readonly string LOG_FILENAME = "logfile.txt";
    private static readonly string LOG_FOLDER = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "App name");
    private static readonly string LOG_FULLPATH = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "App name", LOG_FILENAME);

    public static void LogMessageToFile(string msg)
    {
        msg = string.Format("{0:G}: {1}{2}", DateTime.Now, msg, Environment.NewLine);
        File.AppendAllText(LOG_FULLPATH, msg);
    }
}

作为一个日志功能,我希望能够从代码的许多不同部分访问它(因此,我选择将其设置为静态)。然而,我想要使它线程安全,我需要始终传递一个公共对象来进行锁定(lock()),这样做似乎违背了静态函数的目的。或者这并不是这种情况吗?

为什么不将它设计成单例模式呢? - TomDane
2个回答

23
public static class Logger
{
    private static readonly string LOG_FILENAME = "logfile.txt";
    private static readonly string LOG_FOLDER = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "App name");
    private static readonly string LOG_FULLPATH = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "App name", LOG_FILENAME);

    private static Object theLock=new Object();

    public static void LogMessageToFile(string msg)
    {
        msg = string.Format("{0:G}: {1}{2}", DateTime.Now, msg, Environment.NewLine);
        lock (theLock)
        {
            File.AppendAllText(LOG_FULLPATH, msg);
        }
    }
}

4
锁确保在一个线程进入关键代码部分时,另一个线程不会进入该关键部分。如果另一个线程尝试进入锁定的代码,它将等待、阻塞,直到对象被释放为止。 - Parimal Raj
1
这个解决方案是否比创建一个非静态类更可取,该类接受一个对象(可能有一个构造函数,接受一个字符串:要记录的消息),并对其进行锁定? - Chrsjkigs99
1
+1. @raney - 锁定什么?在写入文件时需要使用lock(每个文件最多只能有一个对象进行锁定,对于示例代码,为所有文件使用一个静态锁是可以接受的)。请注意,对于真正的日志记录,最好使用现有的解决方案。 - Alexei Levenkov
不要锁定可写字段。 因此,theLock 应该是一个只读字段。 请参阅块应在只读字段上同步 - Syntony
由于该字段在对象的生命周期内只分配了一次并锁定在它上面,因此没有任何条件适用。 - Eugen Rieck

2
在您的LogMessageToFile方法中,需要一个锁来防止多线程访问:
private static Object _mylock = new Object();
public static void LogMessageToFile(string msg)
{
    lock(_mylock)
    {
       msg = string.Format("{0:G}: {1}{2}", DateTime.Now, msg, Environment.NewLine);
       File.AppendAllText(LOG_FULLPATH, msg);
    }
}

2
_1_myloc必须是静态的才能在静态方法中访问! - Eugen Rieck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接