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