簡體   English   中英

無鎖線程安全隊列 - 需要建議

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM