1

我已经在网上阅读了很多关于 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# in Depth”文章并尝试了不同的方法但没有奏效。即使使用 .NET 4.0 Lazy 也不能解决问题。唯一成功的解决方法是通过在 _instance 上使用 [ThreadStatic] 属性来打破单例规则,并且不再需要 DCL。但是随后它将类变成了具有延迟初始化的多线程类,不再是真正的 Singleton。

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

4

1 回答 1

6

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

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

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

解决方案:

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

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

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

于 2014-02-13T15:25:56.813 回答