2010-12-21 97 views
4

可以說我有它獲取像每secodC#多線程

void Session_OnEvent(object sender, CustomEventArgs e) 
{ 
    //DoStuff 

    DoLongOperation(e); 
} 

我想的方法DoLongOperation(e)中的10倍觸發的事件;要在分離式螺紋每次會觸發該事件處理,

我可以這樣做:

new Thread(DoLongOperation).Start(e); 

,但我有一種感覺,這是不好的表現,我想達到最佳的性能,因此我能做的最好的事情是什麼?

感謝idvance ..

編輯:當我說長我沒有意思的調​​度研究,將需要超過1秒,最大它只是我不希望事件等待那個時間,所以我想打,在分離式螺紋...

+1

你能做的最好的事情:測量。 – 2010-12-21 15:14:34

+1

你的意思是你想取消以前的LongOperation。或者你想對並行LongOperation有最高限制? – 2010-12-21 15:15:07

+0

使用線程使現代核心上可用的CPU週期數量增加一倍,適用於性能。但是,您的程序中未確診的錯誤數量增加了四倍,對於發貨日期不利。 – 2010-12-21 15:34:41

回答

2

使用一個線程來處理您的請求,併爲您的事件中的線程排隊工作項。

具體:

  • 粘貼E
  • 創建列表< CustomEventArgs>和插入複製到結束從線程到該列表
  • 同步訪問,並從事件

作爲班級的成員對象,請執行以下操作:

List<CustomEventArgs> _argsqueue; 
Thread _processor; 

在類的構造函數,這樣做:

_argsqueue=new List<CustomEventArgs>(); 
_processor=new Thread(ProcessorMethod); 

定義processormethod:

void ProcessorMethod() 
{ 
    while (_shouldEnd) 
    { 
     CustomEventArgs e=null; 
     lock (_argsqueue) 
     { 
      if (_argsqueue.Count>0) 
      { 
       CustomEventArgs e=_argsqueue[0]; 
       _argsqueue.RemoveAt(0); 
      } 
     } 
     if (e!=null) 
     { 
      DoLongOperation(e); 
     } 
     else 
     { 
      Sleep(100); 
     } 
    } 
} 

而在你的事件:

lock (_argsqueue) 
{ 
    _argsqueue.Add(e.Clone()); 
} 

你必須工作的細節對於你自己,例如,在形式關閉或處理有問題的對象時,你必須:

_shouldEnd=true; 
_processor.Join(); 
0

是的,你可以這樣做。但是當您的事件每秒觸發10次並且每秒啓動10次長時間運行操作時,您將很快耗盡線程。

6

直接回答你的問題是:使用the managed thread pool通過利用ThreadPool.QueueUserWorkItem推動你的操作。 (您可能需要查看「when do I use the thread pool vs. my own threads?」這個問題的答案)。

但是,請看大圖:如果您開始的所有操作都需要超過100毫秒才能完成,那麼您在數學上會產生比您可以處理的更多工作。無論你如何切片,這都不會結束。例如,如果您每次創建一個單獨的線程,那麼您的進程將耗盡線程,如果使用線程池,那麼您將使用它永遠無法完成的工作來淹沒它。等等。

如果僅一些您的操作最終會很長,並且會立即完成,那麼您可能有機會獲得實用的解決方案。否則,你需要重新考慮你的程序設計。

1

的表現將在很大程度上取決於幾個因素:

  • 多少個線程將同時運行?
  • 他們會做什麼?
  • 他們會跑多久? (最短執行時間,最大值,平均值)
  • 如果其中一個線程異常終止,會發生什麼情況?

每秒十次是一個相當高的活動率。根據執行的持續時間,使用單獨的進程(如服務)可能更有意義。該活動顯然必須是線程安全的,這意味着(部分)沒有資源爭用。如果兩個線程可能需要更新相同的資源(文件,內存位置),則需要使用鎖定。如果處理不好,這會妨礙效率。

2

如果您使用的是C#4.0,則可能需要考慮使用task scheduler。由於您的DoLongOperation意味着它會長時間運行,你應該考慮以下

長時間運行的任務

你可能想明確地防止 任務被提上本地隊列。 例如,您可能知道某個 特定工作項目將運行 相對較長的時間,並且可能會阻止 本地隊列上的所有其他工作項目。在這種情況下,你可以 指定LongRunning選項, 提供一個提示到 額外的線程可能需要 任務的調度,以便它不會阻止 其他線程 或工作的向前進行本地隊列中的項目。通過 使用此選項可以完全避免 ThreadPool,包括全局和本地隊列的 。

使用TaskScheduler的另一個好處是它具有MaximumConcurrencyLevel。這可以讓你在完成Jon所推薦的測試後相對容易地調整你的併發。

下面是MSDN取樣中做到了這一點

namespace System.Threading.Tasks.Schedulers 
{ 

    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Threading; 

    class Program 
    { 
     static void Main() 
     { 
      LimitedConcurrencyLevelTaskScheduler lcts = new LimitedConcurrencyLevelTaskScheduler(1); 
      TaskFactory factory = new TaskFactory(lcts); 

      factory.StartNew(()=> 
       { 
        for (int i = 0; i < 500; i++) 
        { 
         Console.Write("{0} on thread {1}", i, Thread.CurrentThread.ManagedThreadId); 
        } 
       } 
      ); 

      Console.ReadKey(); 
     } 
    } 

    /// <summary> 
    /// Provides a task scheduler that ensures a maximum concurrency level while 
    /// running on top of the ThreadPool. 
    /// </summary> 
    public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler 
    { 
     /// <summary>Whether the current thread is processing work items.</summary> 
     [ThreadStatic] 
     private static bool _currentThreadIsProcessingItems; 
     /// <summary>The list of tasks to be executed.</summary> 
     private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks) 
     /// <summary>The maximum concurrency level allowed by this scheduler.</summary> 
     private readonly int _maxDegreeOfParallelism; 
     /// <summary>Whether the scheduler is currently processing work items.</summary> 
     private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks) 

     /// <summary> 
     /// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the 
     /// specified degree of parallelism. 
     /// </summary> 
     /// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param> 
     public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism) 
     { 
      if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism"); 
      _maxDegreeOfParallelism = maxDegreeOfParallelism; 
     } 

     /// <summary>Queues a task to the scheduler.</summary> 
     /// <param name="task">The task to be queued.</param> 
     protected sealed override void QueueTask(Task task) 
     { 
      // Add the task to the list of tasks to be processed. If there aren't enough 
      // delegates currently queued or running to process tasks, schedule another. 
      lock (_tasks) 
      { 
       _tasks.AddLast(task); 
       if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism) 
       { 
        ++_delegatesQueuedOrRunning; 
        NotifyThreadPoolOfPendingWork(); 
       } 
      } 
     } 

     /// <summary> 
     /// Informs the ThreadPool that there's work to be executed for this scheduler. 
     /// </summary> 
     private void NotifyThreadPoolOfPendingWork() 
     { 
      ThreadPool.UnsafeQueueUserWorkItem(_ => 
      { 
       // Note that the current thread is now processing work items. 
       // This is necessary to enable inlining of tasks into this thread. 
       _currentThreadIsProcessingItems = true; 
       try 
       { 
        // Process all available items in the queue. 
        while (true) 
        { 
         Task item; 
         lock (_tasks) 
         { 
          // When there are no more items to be processed, 
          // note that we're done processing, and get out. 
          if (_tasks.Count == 0) 
          { 
           --_delegatesQueuedOrRunning; 
           break; 
          } 

          // Get the next item from the queue 
          item = _tasks.First.Value; 
          _tasks.RemoveFirst(); 
         } 

         // Execute the task we pulled out of the queue 
         base.TryExecuteTask(item); 
        } 
       } 
       // We're done processing items on the current thread 
       finally { _currentThreadIsProcessingItems = false; } 
      }, null); 
     } 

     /// <summary>Attempts to execute the specified task on the current thread.</summary> 
     /// <param name="task">The task to be executed.</param> 
     /// <param name="taskWasPreviouslyQueued"></param> 
     /// <returns>Whether the task could be executed on the current thread.</returns> 
     protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 
     { 
      // If this thread isn't already processing a task, we don't support inlining 
      if (!_currentThreadIsProcessingItems) return false; 

      // If the task was previously queued, remove it from the queue 
      if (taskWasPreviouslyQueued) TryDequeue(task); 

      // Try to run the task. 
      return base.TryExecuteTask(task); 
     } 

     /// <summary>Attempts to remove a previously scheduled task from the scheduler.</summary> 
     /// <param name="task">The task to be removed.</param> 
     /// <returns>Whether the task could be found and removed.</returns> 
     protected sealed override bool TryDequeue(Task task) 
     { 
      lock (_tasks) return _tasks.Remove(task); 
     } 

     /// <summary>Gets the maximum concurrency level supported by this scheduler.</summary> 
     public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } } 

     /// <summary>Gets an enumerable of the tasks currently scheduled on this scheduler.</summary> 
     /// <returns>An enumerable of the tasks currently scheduled.</returns> 
     protected sealed override IEnumerable<Task> GetScheduledTasks() 
     { 
      bool lockTaken = false; 
      try 
      { 
       Monitor.TryEnter(_tasks, ref lockTaken); 
       if (lockTaken) return _tasks.ToArray(); 
       else throw new NotSupportedException(); 
      } 
      finally 
      { 
       if (lockTaken) Monitor.Exit(_tasks); 
      } 
     } 
    } 
}