具有双重检查锁定的单例 - 错误“被另一个进程使用”

时间:2014-02-13 15:12:53

标签: c# multithreading double-checked-locking

我一直在阅读很多关于Singleton在线的文章,但大多数文章只展示了创建简单对象并正确锁定以确保线程安全而没有竞争条件。

简单到足以演示,我在那里放了一个文件记录方法。它将适用于10-20个并发线程,但是当我尝试100个线程时,它将失败,因为“进程无法访问该文件,因为它正被另一个进程使用”。

public class Logger
{

    private string _path = "c:\\temp\\MyTestLog.txt";

    // Singleton with padlock for DCL
    private volatile static Logger _instance;
    private static readonly object  _padLock = new Object();

    // Singleton
    public static Logger Instance()
    {
        // Implement Double Check Locking (MSDN - Thread-safe)
        if (_instance == null)
        {
            lock (_padLock)
            {
                if (_instance == null)
                    _instance = new Logger();
            }
        }

        return _instance;
    }

    // Simple WriteLog 
    public void WriteLog(string text, out bool result)
    {
        try
        {             
            // Write to file 
            using (Stream stream = File.Open(_path, FileMode.Append))
            {
                using (TextWriter writer = new StreamWriter(stream))
                {
                    writer.WriteLine(text);
                }
            }

            Debug.WriteLine(text);
            result = true;
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.ToString());
            result = false;
        }
    }

}

要从客户端调用此方法,我设置了一个简单的Windows窗体和线程,通过调用生成多线程:

Logger.Instance().WriteLog("test", out result);

好吧,我知道它失败的原因是因为之前的_instance被传递到下一个内部文件操作未完成导致错误的线程。但不知道如何解决这个问题。

我也尝试了这个..如果我用SqlConnection替换文件IO写入,我将得到“连接未关闭。连接的当前状态是打开的”而不是“进程因为正在使用而无法访问该文件通过另一个过程“。同样的想法。当然你会使用Connection Pool而不是Singleton,但仅仅是为了我自己的好奇心。

我读了“C#in Depth”一文并尝试了不同的方法,但没有奏效。即使使用.NET 4.0,Lazy也无法解决问题。成功的唯一解决方法是通过在_instance上使用[ThreadStatic]属性来破坏Singleton规则,并且不再需要DCL。但随后它将类转换为具有延迟初始化的多线程类,而不再是真正的Singleton。

所以,我想知道...... Singleton如何解决这个问题?

1 个答案:

答案 0 :(得分:6)

如评论中所述,您的问题是WriteLog根本不是线程安全的。

拥有单例并不意味着该类的每个方法都在同一个线程中。

即使你只有一个实例,它的方法仍然可以被多个线程同时调用。

解决方案:

在WriteLog方法中放入一些同步逻辑。锁定声明,ReaderWriterLockSlim ......选择全是你的!

如果您想了解有关c#中的线程的更多信息,我推荐这篇文章:

http://www.albahari.com/threading/