2

我需要设计一个线程安全的记录器。我的记录器必须有一个 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
 }
}    
}
4

3 回答 3

5

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

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

我会从这个页面推荐“阻塞队列” 。

在类本身中包含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
            }
        } 
    }
}

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

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

于 2010-01-12T12:19:26.013 回答
3

lock您的分析器是否通过使用简单的语句向您显示您正在经历大量开销?无锁编程很难做到正确,如果你真的需要它,我建议从可靠来源获取一些现有的东西。

于 2010-01-12T12:45:42.093 回答
1

如果您有原子操作,则不难做到无锁。取一个单链表;你只需要指针。

Log function:
1. Locally prepare the log item (node with logging string).
2. Set the local node's next pointer to head.
3. ATOMIC: Compare head with local node's next, if equal, replace head with address of local node.
4. If the operation failed, repeat from step 2, otherwise, the item is in the "queue".

Worker:
1. Copy head locally.
2. ATOMIC: Compare head with local one, if equal, replace head with NULL.
3. If the operation failed, repeat from step 1.
4. If it succeeded, process the items; which are now local and out of the "queue".

于 2010-01-12T14:10:53.937 回答