C#入队失败

时间:2015-03-17 18:15:45

标签: c# multithreading queue

我有一个简单的日志记录机制,应该是线程安全的。它大部分时间都有效,但我不时会在这一行上得到一个例外,“_ 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(字符串s)。 ProcessLogQ对记录器类是私有的。

*编辑* 我没有提到这是在.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);
    }

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

查看TaskParallel库。所有的努力都已经为你完成了。如果您这样做是为了了解多线程读取锁定技术以及每种技术的优缺点。

此外,你正在检查锁定语句之外的_logQ是否为null,我可以推断出它是一个静态构造函数中未初始化的静态字段。你可以避免做这个空检查(它应该在一个锁中,它是关键代码!)你可以通过使它成为静态只读并在静态构造函数中初始化来确保线程安全。

此外,您没有正确处理队列状态。由于在检查队列计数期间没有锁定,因此每次迭代都会有所不同。你错过了一个锁定作为你的出队物品。

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