[英]Lock-free thread-safe queue - need advice
我需要設計一個線程安全的記錄器。 我的記錄器必須有一個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
}
}
}
“無鎖”並不意味着線程不會相互阻塞。 這意味着它們通過非常有效但非常棘手的機制相互阻擋。 只有非常高性能的情況才需要,甚至專家也會錯誤(很多)。
最好的建議:忘記“無鎖”,只使用“線程安全”隊列。
我會從這個頁面推薦“阻止隊列”。
在類本身中包含ThreadRoutine
(Consumer)是一個選擇問題。
對於問題的第二部分,它取決於“某些同步事件”究竟是什么。 如果您打算使用Method調用,那么讓它啟動一次性線程。 如果你想等待信號量而不是使用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 sample的最后一次迭代(不完整)顯示了如何僅鎖定Enqueue和Dequeue調用。 我認為這將足夠快。
您的探查器是否通過使用簡單的lock
語句向您顯示您正在經歷大量開銷? 無鎖編程很難做到,如果你真的需要它,我會建議從可靠的來源獲取現有的東西。
如果你有原子操作,這不難做到無鎖。 拿一個單鏈表; 你只需要頭指針。
記錄功能:
1.本地准備日志項(帶有日志字符串的節點)。
2.將本地節點的下一個指針設置為head 。
3. 原子:將頭與本地節點的下一個比較,如果相等,則用本地節點的地址替換頭 。
4.如果操作失敗,請從步驟2開始重復,否則,該項目位於“隊列”中。
工人:
1.在本地復制頭部 。
2. 原子:將頭與本地頭比較,如果相等,用NULL替換頭 。
3.如果操作失敗,請從步驟1開始重復。
4.如果成功,處理項目; 現在是本地的,並且在“隊列”之外。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.