C# 入队失败

C# Enqueue Failure

我有一个简单的日志记录机制,应该是线程安全的。它大部分时间都有效,但有时我会在这一行出现异常,“_logQ.Enqueue(s);”队列不够长。在调试器中查看有时只有数百个项目,所以我看不到它是资源。队列应该根据需要扩展。如果我捕获异常而不是让调试器在异常处暂停,我会看到同样的错误。这里有什么不是线程安全的吗?我什至不知道如何开始调试它。

    static void ProcessLogQ(object state)
    {
        try
        {
            while (_logQ.Count > 0)
            {
                var s = _logQ.Dequeue();
                string dir="";
                Type t=Type.GetType("Mono.Runtime");
                if (t!=null)
                {
                    dir ="/var/log";
                }else
                {
                    dir = @"c:\log";
                    if (!Directory.Exists(dir))
                        Directory.CreateDirectory(dir);
                }
                if (Directory.Exists(dir))
                {
                    File.AppendAllText(Path.Combine(dir, "admin.log"), DateTime.Now.ToString("hh:mm:ss ") + s + Environment.NewLine);
                }
            }
        }
        catch (Exception)
        {
        }
        finally
        {
            _isProcessingLogQ = false;
        }
    }

    public static void Log(string s) {
        if (_logQ == null)
            _logQ = new Queue<string> { };

        lock (_logQ)
            _logQ.Enqueue(s);
        if (!_isProcessingLogQ) {
            _isProcessingLogQ = true;
            ThreadPool.QueueUserWorkItem(ProcessLogQ);
        }
    }

注意线程都调用了Log(string s)。 ProcessLogQ 是记录器私有的 class.

* 编辑 * 我犯了一个错误,没有提到这是在 .NET 3.5 环境中,因此我不能使用 Task 或 ConcurrentQueue。我正在为 .NET 3.5 约束内的当前示例修复。

** 编辑 * 我相信我有下面列出的 .NET 3.5 的线程安全版本。我在程序启动时从单个线程启动记录器线程一次,因此只有一个线程 运行 记录到文件(t 是静态线程):

    static void ProcessLogQ()
    {
        while (true) {
            try {
                lock (_logQ);
                while (_logQ.Count > 0) {
                    var s = _logQ.Dequeue ();
                    string dir = "../../log";
                    if (!Directory.Exists (dir))
                        Directory.CreateDirectory (dir);
                    if (Directory.Exists (dir)) {
                        File.AppendAllText (Path.Combine (dir, "s3ol.log"), DateTime.Now.ToString ("hh:mm:ss ") + s + Environment.NewLine);
                    }
                }
            } catch (Exception ex) {
                Console.WriteLine (ex.Message);
            } finally {
            }
            Thread.Sleep (1000);
        }
    }

    public static void startLogger(){
        lock (t) {
            if (t.ThreadState != ThreadState.Running)
                t.Start ();
        }
    }
    private static void multiThreadLog(string msg){
        lock (_logQ)
            _logQ.Enqueue(msg);
    }

对于线程安全队列,您应该改用 ConcurrentQueue:

https://msdn.microsoft.com/en-us/library/dd267265(v=vs.110).aspx

查看 TaskParallel 库。所有的艰苦工作都已经为你完成了。如果您这样做是为了了解多线程,请阅读锁定技术以及每种技术的优缺点。

此外,您在 lock 语句之外检查 _logQ 是否为 null,据我推断,它是一个静态字段,您没有在静态构造函数中进行初始化。您可以避免执行此空检查(它应该在锁内,它是关键代码!)您可以通过将其设为静态只读并在静态构造函数中对其进行初始化来确保线程安全。

此外,您没有正确处理队列状态。由于在检查队列计数期间没有锁定,因此每次迭代都可能有所不同。您在出列项目时缺少锁。

优秀资源: http://www.yoda.arachsys.com/csharp/threads/