2012-02-18 3 views
0

특히, 나는 궁금해하고있어 :이 백그라운드 스레드 큐는 실행 가능한 구현입니까?

대기 상태에있는 동안 ManualResetEvent가 리소스를 소비합니까? 컨텍스트 스위칭의 성능 저하가 대기 상태에있는 스레드에 적용됩니까?

더 적은 작업을 수행하는 여러 BackgroundThreadQueues를 사용하거나 더 많은 작업을 수행하는 하나의 BackgroundThreadQueue를 사용할 수 있고 여러 개를 사용하도록 선택할 경우 대기중인 스레드 대기열은 아무 것도 수행하지 않는 동안 프로세스 성능에 영향을 미칩니 까? ?

C# 또는 다른 잠금 전략에서 사용해야하는 더 나은 FIFO 스레드 큐가 있습니까?

모든 의견을 환영합니다.

/// <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

특정 perf 문제가 있습니까, 아니면 그냥 "여기 코드가 다르고 검토 할 수 있습니까?"라는 요청입니까? codereview.stackexchange.com 사이트가 가장 좋습니다. –

+0

Hans에 동의해야합니다. codereview에 대한 것으로 보입니다. – Lloyd

+0

글쎄, 나는 스택 오버플로에 대해 많은 질문을하지 않고있다. 나는 이것을 게시하기 전에 FAQ를 검토했다. 요구 사항 중 하나는 다음과 같다 : "일반적으로 질문이 ... 소프트웨어 알고리즘을 다루는 경우" 이것이 무엇입니까. 그리고, .net 객체의 기능에 관해서는 ManualResetEvent에 대한 질문이 있습니다. –

답변

0

대기하는 스레드의 단순한 존재는 어떤 회계 정보 런타임 또는 커널이 그들에 대한 계속 진행중인 성능 스레드의 실제 스택을 수용하기 위해 예약 된 메모리를 넘어 미치는 영향, 그리고이 없습니다. 따라서 진정으로 대기중인 스레드는 RAM을 사용하는 것 외에는 아무 것도하지 않습니다.

그렇다면 .Net에는 threadpool이 내장되어 있고이 코드를 직접 작성하는 것이 좋을지 모르겠지만 사용자가 선호하는 동시성 도구를 사용해야합니다.

+0

.Net에는 ThreadPool이 내장되어 있지만, 풀을 만들 수 없다면 스레드 생성에는 0.5 초가 걸립니다. 스레드의 사용자는 기다릴 필요가 없습니다. 정확히 어떤 동시성 도구를 언급하고 있습니까? 순서대로 작업을 수행하려면 별도의 스레드가 필요하며 이러한 작업이 완료되는 시점을 알아야합니다. 특정 제안이 있으면 사전 제작 된 것을 사용하는 것이 좋습니다. –

+3

나는 Task Parallel Library를 살펴볼 것을 제안한다. 작업은 작업 인스턴스로 표시되며 작업간에 종속성을 나타 내기 위해 함께 연결될 수 있습니다. 따라서 작업이 끝나면 부양 가족이 운영됩니다. – wasabi

+0

다른 것들을 위해 작업 병렬 라이브러리를 사용합니다. 위의 설명에서 언급 한 사항과 연속성이 동일한 스레드에 있음을 보증하지 않기 때문에이 작업은 효과가 없습니다. (틀 렸으면 고쳐줘). –

1

ManualResetEvent.WaitOne()에 대한 호출에서 차단 된 스레드는 CPU에서 제거되며 깨우쳐야 할 이벤트 (즉 Set()에 대한 호출)가 발생할 때까지 OS에 의한 스케줄링을 위해 다시 고려되지 않습니다. 따라서 신호를 기다리는 동안은 비활성 상태이며 CPU주기를 소모하지 않습니다.

관련 문제