【问题标题】:Singleton with Double Check Locking - error "being used by another process"带有双重检查锁定的单例 - 错误“正在被另一个进程使用”
【发布时间】:2014-02-13 23:25:56
【问题描述】:

我已经在网上阅读了很多关于 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写入,我会得到“连接没有关闭。连接的当前状态是打开”而不是“进程无法访问文件,因为它正在被使用通过另一个过程”。同样的想法。当然,您将使用连接池而不是单例,但这只是出于我自己的好奇心。

我阅读了“深入了解 C#”的文章并尝试了不同的方法,但没有成功。即使使用 .NET 4.0 Lazy 也不能解决问题。唯一成功的解决方法是通过在 _instance 上使用 [ThreadStatic] 属性来打破单例规则,并且不再需要 DCL。但随后它将类变成了一个具有延迟初始化的多线程类,不再是真正的 Singleton。

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

【问题讨论】:

  • 您提供的锁定机制确保只有一个 Logger 实例存在(假设它是正确的)......但是您没有采取任何措施来阻止 WriteLog 中的 100 个线程在一次!
  • @AakashM 是对的。您正在尝试同时写入 same file。 SQL 将创建一个新连接,您不会在那里共享同一个连接
  • 问题不在于单例实现。问题出在WriteLog 方法中:多个线程可以访问它并同时打开/写入/关闭文件。您应该考虑重用相同的TextWriter 实例并同步对该方法的访问。
  • 还请记住,.NET 具有应用程序域的概念。如果您决定将此Logger 类放入实用程序DLL 中,然后从其他DLL 中调用它,那么单例模式会中断。在我们研究跨应用程序域单例以针对这种情况提供更强大的解决方案之前,我们对此感到厌烦。

标签: c# multithreading double-checked-locking


【解决方案1】:

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

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

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

解决方案:

在你的 WriteLog 方法中加入一些同步逻辑。 Lock 语句,ReaderWriterLockSlim……选择权在你!

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

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

【讨论】:

  • 非常感谢!!!所以,基本上我在 WriteLog 方法中添加了这个属性,它似乎解决了这个问题!你们太有帮助了。 [MethodImpl(MethodImplOptions.Synchronized)]
猜你喜欢
  • 1970-01-01
  • 2013-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-26
  • 1970-01-01
相关资源
最近更新 更多