c# - 无锁线程安全队列——需要建议

标签 c# logging thread-safety lock-free

我需要设计一个线程安全的记录器。我的记录器必须有一个 Log() 方法,它只是将要记录的文本排入队列。此外,记录器必须是无锁的——以便其他线程可以在不锁定记录器的情况下记录消息。我需要设计一个必须等​​待的工作线程 对于某些同步事件,然后使用标准 .NET 日志记录(这不是线程安全的)记录队列中的所有消息。所以我感兴趣的是工作线程的同步 - 和日志功能。下面是我设计的类的草图。我想我必须在这里使用 Monitor.Wait/Pulse 或任何其他方式来暂停和恢复工作线程。我不想在没有记录器工作时花费 CPU 周期。

让我换一种说法 - 我想设计一个记录器,它不会阻塞使用它的调用线程。我有一个高性能系统——这是一个要求。

class MyLogger
{
  // This is a lockfree queue - threads can directly enqueue and dequeue
  private LockFreeQueue<String> _logQueue;
  // worker thread
  Thread _workerThread;
  bool _IsRunning = true;

 // this function is used by other threads to queue log messages
  public void Log(String text)
{
  _logQueue.Enqueue(text);
}

// this is worker thread function
private void ThreadRoutine()
{
 while(IsRunning)
 {
   // do something here
 }
}    
}

最佳答案

“无锁”并不意味着线程不会互相阻塞。这意味着它们通过非常有效但也非常棘手的机制相互阻止。仅在非常高性能的场景中需要,即使是专家也会出错(很多)。

最佳建议:忘记“无锁”,只使用“线程安全”队列。

我会推荐 this page 中的“阻塞队列” .

在类本身中包含 ThreadRoutine(消费者)是一个选择问题。

对于你问题的第二部分,这取决于“一些同步事件”到底是什么。如果您打算使用方法调用,那么让它启动一个一次性线程。如果您想在信号量上等待,不要使用 Monitor 和 Pulse。他们在这里不可靠。使用 AutoResetEvent/ManualResetEvent。
如何显示取决于您希望如何使用它。

你的基本成分应该是这样的:

class Logger
{
    private AutoResetEvent _waitEvent = new AutoResetEvent(false);
    private object _locker = new object();
    private bool _isRunning = true;    

    public void Log(string msg)
    {
       lock(_locker) { _queue.Enqueue(msg); }
    }

    public void FlushQueue()
    {
        _waitEvent.Set();
    }

    private void WorkerProc(object state)
    {
        while (_isRunning)
        {
            _waitEvent.WaitOne();
            // process queue, 
            // ***
            while(true)
            {
                string s = null;
                lock(_locker)
                {
                   if (_queue.IsEmpty) 
                      break;
                   s = _queue.Dequeu();
                }
                if (s != null)
                  // process s
            }
        } 
    }
}

部分讨论似乎是在处理Queue时要做什么(标记为***)。您可以锁定队列并处理所有项目,在此期间将阻止(更长时间)添加新条目,或者逐个锁定和检索条目并且每次只锁定(非常)短时间。我已经添加了最后一个场景。

总结:您需要的不是无锁解决方案,而是无 block 解决方案。 Block-Free 不存在,您将不得不接受尽可能少的阻塞。 mys 示例的最后一次迭代(不完整)显示了如何仅锁定入队和出队调用。我认为这将足够快。

关于c# - 无锁线程安全队列——需要建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2048785/

相关文章:

c# - Entity Framework 4,插入外键值而不是 ID

c# - Visual Studio : [DebuggerDisplay], 在指定格式时显示 "not a valid format specifier"

java - GWT 记录器 : No control over debug output?

c - 访问C数组线程中的不同元素是否安全?

Java同步静态列表?

c# - 模型是否包含字段而不将其添加到数据库?

c# - 如何抑制 StyleCop 警告?

arrays - 计算单元格变化

logging - Ideolog(PyCharm):如何为标准日志记录库配置日志格式

java - 生产者/消费者线程并发 Java