多线程文件写入队列

时间:2009-06-30 15:32:56

标签: c# multithreading file-io

所以我有一个静态类,应该用作日志文件管理器,能够将“消息”(字符串)添加到Queue对象,并将消息推送到文件。麻烦的是,许多不同的线程应该入队,并且编写者也需要异步。目前,当我插入队列时,我还要检查编写器是否正在编写(bool检查),如果不是,我设置bool然后开始编写,但是我得到关于文件访问的间歇性IO异常,有时甚至是奇怪的写作行为。

有人想帮我这个吗?

5 个答案:

答案 0 :(得分:2)

我只有一个线程在执行写操作以避免争用,而我会使用多个线程进行排队。

建议您“为了保证Queue的线程安全,所有操作必须通过Synchronized方法返回的包装器完成。” - 来自http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx

答案 1 :(得分:2)

听起来这个队列正在驱动文件写入操作。我建议您反转控制关系,以便编写器驱动进程并检查队列是否正常工作。

实现这一点的最简单方法是向编写器添加一个轮询机制,在该编写器中,它定期检查队列的工作。

或者,您可以创建一个可观察的队列类,只要队列从空转换就会通知订阅者(编写者):订阅编写者可以开始工作。 (此时,作者还应该取消订阅队列的广播,否则会改变它对队列警报的反应方式。)

完成作业后,编写器会检查队列是否有更多工作。如果没有其他工作要做,它将进入休眠状态并恢复轮询或进入休眠状态并重新订阅队列的警报。

作为Irwin noted in his answer,您还需要使用Queue类'Synchronized方法提供的线程安全包装器,或者如果有多个线程,则手动同步对Queue的访问权限正在阅读并写信给它(as in SpaceghostAli's example)。

答案 2 :(得分:2)

您应该在队列周围进行同步。让多个线程发送到队列,并从队列中读取单个线程并写入该文件。

public void Log(string entry)
{
    _logMutex.WaitOne();
    _logQueue.Enqueue(entry);
    _logMutex.ReleaseMutex();
}

private void Write()
{
    ...
    _logMutex.WaitOne();
    string entry = _logQueue.Dequeue();
    _logMutex.ReleaseMutex();

    _filestream.WriteLine(entry);
    ...
}

答案 3 :(得分:1)

如果您不想像我在其他答案中建议的那样大幅重构您的代码,您可以尝试这个,假设您的LogManager班级有:

  • 静态线程安全队列_SynchronizedQueue
  • 写入时要锁定的静态对象_WriteLock

和这些方法:

public static void Log(string message) {
    LogManager._SynchronizedQueue.Enqueue(message);
    ThreadPool.QueueUserWorkItem(LogManager.Write(null));
}

// QueueUserWorkItem accepts a WaitCallback that requires an object parameter
private static void Write(object data) {
    // This ensures only one thread can write at a time, but it's dangerous
    lock(LogManager._WriteLock) {
        string message = (string)LogManager._SynchronizedQueue.Dequeue();
        if (message != null) {
            // Your file writing logic here
        }
    }
}

只有一个问题:上面Write方法中的lock语句将保证一次只能写一个线程,但这很危险。尝试写入文件时可能会出错,并且您不希望无限期地保留(阻塞)线程池线程。因此,您需要使用允许您指定超时的同步对象,例如Monitor,并重写Write方法,如下所示:

private static void Write() {
    if (!Monitor.TryEnter(LogManager._WriteLock, 2000)) {
       // Do whatever you want when you can't get a lock in time
    } else {
      try {
         string message = (string)LogManager._SynchronizedQueue.Dequeue();
         if (message != null) {
             // Your file writing logic here
         }
      }
      finally {
        Monitor.Exit(LogManager._WriteLock);
      }
    }
}

答案 4 :(得分:1)

让我在不同的层面解决问题:

如果您正在编写业务应用程序,那么您需要关注业务逻辑部分而不是基础架构代码,如果此基础架构代码已经可用,测试并部署到多个生产站点(请注意NFRs的)

我确信您已经意识到log4net和其他http://csharp-source.net/open-source/logging等日志框架的存在。

在手动推出自己的Logger之前,您是否有机会获得这些机会?

将此选项带给您正在编写的企业的技术架构师,并看到她的想法。

干杯