2012-02-18 19 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周期。