2012-02-18 20 views
0

具體而言,我想知道:這是背景線程隊列高性能實現嗎?

ManualResetEvent在處於等待狀態時會消耗資源嗎?上下文切換的性能下降是否適用於處於等待狀態的線程?

如果我可以選擇使用多個BackgroundThreadQueues來減少每個工作,或者使用一個BackgroundThreadQueue來做更多的工作,並且我選擇使用多個...將等待的線程隊列影響進程的性能,而他們沒有做任何事情?

是否有更好的FIFO線程隊列,我應該在C#中使用,還是採用不同的鎖定策略?

任何建議表示讚賞。

/// <summary> 
/// This class is responsible for peforming actions in a FIFO order on a 
/// background thread. When it is constructed, a background thread is created 
/// and a manual reset event is used to trigger actions to be performed when 
/// a new action is enqueued, or when one finishes. There is a ShuttingDown 
/// flag that is set by calling code when it is time to destroy the thread, 
/// and a QueueIsEmpty event is fired whenever the queue finishes executing 
/// the last action. 
/// </summary> 
public class BackgroundThreadQueue : IBackgroundThreadQueue 
{ 
    #region Fields 

    private readonly Queue<Action> queueOfActions = new Queue<Action>(); 
    readonly ManualResetEvent resetEvent; 
    private bool shuttingDown; 
    private bool readyToShutdown; 
    private readonly object lockObject = new object(); 
    private string queuName; 

    #endregion Fields 

    #region Events 

    /// <summary> 
    /// Occurs when the BackgroundThreadQueue is empty, and ready to shut down. 
    /// </summary> 
    public event EventHandler IsReadyToShutdown; 

    #endregion Events 

    #region Constructor 

    public BackgroundThreadQueue(string threadName) 
    { 
     this.resetEvent = new ManualResetEvent(false); 
     queuName = threadName; 
     StartThread(); 
    } 

    #endregion Constructor 

    #region Public Methods 

    public void ClearQueue() 
    { 
     lock (lockObject) 
     { 
      queueOfActions.Clear(); 
     } 
     resetEvent.Set(); 
    } 

    /// <summary> 
    /// Enqueues an action, and calls set on the manual reset event to trigger 
    /// the action to be performed (if no action is currently being performed, 
    /// the one just enqueued will be done immediately, if an action is already 
    /// being performed, then the one just enqueued will have to wait its turn). 
    /// </summary> 
    public void EnqueueAction(Action actionToEnqueue) 
    { 
     if (actionToEnqueue == null) 
     { 
      throw new ArgumentNullException("actionToEnqueue"); 
     } 

     bool localReadyToShutDown = false; 
     lock (lockObject) 
     { 
      queueOfActions.Enqueue(actionToEnqueue); 

      if(this.readyToShutdown) 
      { 
       localReadyToShutDown = true; 
       this.readyToShutdown = false; 
      } 
     } 

     //if this instance is ready to shut down...and we just enqueued a 
     //new action...we can't shut down now... 
     if (localReadyToShutDown) 
     { 
      StartThread(); 
     } 
     resetEvent.Set(); 
    } 

    #endregion Public Methods 

    #region Public Properties 

    public bool ReadyToShutdown 
    { 
     get 
     { 
      lock (lockObject) 
      { 
       return this.shuttingDown && this.readyToShutdown; 
      } 
     } 
     private set 
     { 
      this.readyToShutdown = value; 
      if (this.readyToShutdown) 
      { 
       //let interested parties know that the queue is now empty 
       //and ready to shutdown 
       IsReadyToShutdown.Raise(this); 
      } 
     } 
    } 

    /// <summary> 
    /// Gets or sets a value indicating whether or not the queue should shut down 
    /// when it is finished with the last action it has enqueued to process. 
    /// If the queues owner is shutting down, it needs to notify the queue, 
    /// and wait for a QueueIsEmpty event to be fired, at which point the reset 
    /// event will exit ... the owner shouldn't actually destroy the queue 
    /// until all actions have been performed. 
    /// </summary> 
    public bool ShuttingDown 
    { 
     get 
     { 
      lock (lockObject) 
      { 
       return this.shuttingDown; 
      } 
     } 
     set 
     { 
      lock (lockObject) 
      { 
       bool startThread = false; 
       if (value == false) 
       { 
        readyToShutdown = false; 
        //if we were shutting down...but, now are not 
        startThread = this.shuttingDown; 
       } 

       this.shuttingDown = value; 

       //if we were shutting down, but now are not... 
       //we need to restart the processing actions thread 
       if (startThread) 
       { 
        StartThread(); 
       } 
      } 

      this.resetEvent.Set(); 
     } 
    } 

    #endregion Public Properties 

    #region Private Methods 

    private void StartThread() 
    { 
     var processActionsThread = new Thread(this.ProcessActions); 
     processActionsThread.Name = queuName; 
     processActionsThread.IsBackground = true; 
     processActionsThread.Start();    
    } 

    /// <summary> 
    /// Processes the actions in a while loop, resetting a ManualResetEvent that 
    /// is triggered in the EnqueueAction method and ShuttingDown property. 
    /// </summary> 
    private void ProcessActions() 
    { 
     while (true) 
     { 
      Action action = null; 
      lock (lockObject) 
      { 
       //if there are any actions, then get the first one out of the queue 
       if (queueOfActions.Count > 0) 
       { 
        action = queueOfActions.Dequeue(); 
       } 
      } 
      if (action != null) 
      { 
       action(); 
      } 
      lock (lockObject) 
      { 
       //if any actions were added since the last one was processed, go 
       //back around the loop and do the next one 
       if (this.queueOfActions.Count > 0) 
       { 
        continue; 
       } 

       if (this.shuttingDown) 
       { 
        //ReadyToShutdown setter will raise IsReadyToShutdown 
        ReadyToShutdown = true; 
        //get out of the method if the user has chosen to shutdown, 
        //and there are no more actions to process 
        return; 
       }      
       this.resetEvent.Reset(); 
      } 

      this.resetEvent.WaitOne(); 
     } 
    } 

    #endregion Private Methods 
} 
+2

你有一個特定的性能問題,或者這只是一個「這是一個代碼blob,檢查它」的請求? codereview.stackexchange.com網站是最好的。 – 2012-02-18 17:48:25

+0

不得不同意漢斯,看起來像codereview的東西。 – Lloyd 2012-02-18 17:52:21

+0

那麼,我不會問Stack Overflow上的很多問題,並且在發佈這個...之前我已經回顧了常見問題......其中規定了一個要求:「如果您的問題通常涵蓋...軟件算法」這是什麼。而且,我有關於.net對象功能的具體問題:ManualResetEvent。 – 2012-02-18 18:09:57

回答

0

等待線程的存在本身應該有超出保留容納線程的堆棧實際內存沒有正在進行的性能影響,以及任何會計信息的運行時間或內核保持對於他們。所以不,真正等待的線程除了消耗RAM之外不會做任何事情。

這就是說,我不確定爲什麼要編寫這些代碼,因爲.Net內建了一個線程池,並且您應該更喜歡自己的併發工具。

+0

我意識到.Net有一個內置的ThreadPool,但是,如果池中沒有可用的池,則創建一個線程需要半秒。線程的消費者無法冒險等待。 你指的是什麼併發工具? 我需要單獨的線程按順序執行操作,並且我需要知道這些操作何時完成。如果您有特定的建議,我肯定會更喜歡使用預先構建的東西。 – 2012-02-18 17:33:03

+3

我建議看看任務並行庫。您的操作由Task實例表示,並且它們可以串在一起表示任務之間的依賴關係。所以,當完成時,它的依賴者將運行。 – wasabi 2012-02-18 17:46:26

+0

我使用任務並行庫來處理其他事情。由於上面評論中提到的事情,以及繼續不能保證在同一個線程中的事實,它不適用於此。 (如我錯了請糾正我)。 – 2012-02-18 18:23:32

1

在調用ManualResetEvent.WaitOne()時被阻塞的線程從CPU中被取消,並且不會再被OS調度,直到應該喚醒它的事件(即調用Set())爲止。因此,在等待被髮信號時,它們處於不活動狀態並且不消耗CPU週期。