LimitedTaskScheduler:

using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Utils { public class LimitedTaskScheduler : TaskScheduler, IDisposable { #region 外部方法 [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")] public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize); #endregion #region 變量屬性事件 private BlockingCollection<Task> _tasks = new BlockingCollection<Task>(); List<Thread> _threadList = new List<Thread>(); private int _threadCount = 0; private int _timeOut = Timeout.Infinite; private Task _tempTask; #endregion #region 構造函數 public LimitedTaskScheduler(int threadCount = 10) { CreateThreads(threadCount); } #endregion #region override GetScheduledTasks protected override IEnumerable<Task> GetScheduledTasks() { return _tasks; } #endregion #region override TryExecuteTaskInline protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return false; } #endregion #region override QueueTask protected override void QueueTask(Task task) { _tasks.Add(task); } #endregion #region 資源釋放 /// <summary> /// 資源釋放 /// 如果尚有任務在執行,則會在調用此方法的線程上引發System.Threading.ThreadAbortException,請使用Task.WaitAll等待任務執行完畢后,再調用該方法 /// </summary> public void Dispose() { _timeOut = 100; foreach (Thread item in _threadList) { item.Abort(); } _threadList.Clear(); GC.Collect(); GC.WaitForPendingFinalizers(); if (Environment.OSVersion.Platform == PlatformID.Win32NT) { SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1); } } #endregion #region 創建線程池 /// <summary> /// 創建線程池 /// </summary> private void CreateThreads(int? threadCount = null) { if (threadCount != null) _threadCount = threadCount.Value; _timeOut = Timeout.Infinite; for (int i = 0; i < _threadCount; i++) { Thread thread = new Thread(new ThreadStart(() => { Task task; while (_tasks.TryTake(out task, _timeOut)) { TryExecuteTask(task); } })); thread.IsBackground = true; thread.Start(); _threadList.Add(thread); } } #endregion #region 全部取消 /// <summary> /// 全部取消 /// </summary> public void CancelAll() { while (_tasks.TryTake(out _tempTask)) { } } #endregion } }
ThreadHelper(Run方法沒有使用LimitedTaskScheduler,Run2方法使用了LimitedTaskScheduler):

using System; using System.Windows.Threading; using System.Threading; using System.Threading.Tasks; namespace Utils { /// <summary> /// 線程幫助類(處理單線程任務) /// </summary> public class ThreadHelper { private static LimitedTaskScheduler _defaultScheduler = new LimitedTaskScheduler(); /// <summary> /// 執行 /// 例:ThreadHelper.Run(() => { }, (ex) => { }); /// </summary> /// <param name="doWork">在線程中執行</param> /// <param name="errorAction">錯誤處理</param> public static System.Threading.Tasks.Task Run2(Action doWork, LimitedTaskScheduler scheduler = null, Action<Exception> errorAction = null) { if (scheduler == null) scheduler = _defaultScheduler; System.Threading.Tasks.Task task = System.Threading.Tasks.Task.Factory.StartNew(() => { try { if (doWork != null) { doWork(); } } catch (Exception ex) { if (errorAction != null) errorAction(ex); LogUtil.LogError(ex); } }, CancellationToken.None, TaskCreationOptions.None, scheduler); return task; } /// <summary> /// 執行 /// 例:ThreadHelper.Run(() => { }, (ex) => { }); /// </summary> /// <param name="doWork">在線程中執行</param> /// <param name="errorAction">錯誤處理</param> public static System.Threading.Tasks.Task Run(Action doWork, LimitedTaskScheduler scheduler = null, Action<Exception> errorAction = null) { System.Threading.Tasks.Task task = System.Threading.Tasks.Task.Factory.StartNew(() => { try { if (doWork != null) { doWork(); } } catch (Exception ex) { if (errorAction != null) errorAction(ex); LogUtil.LogError(ex); } }); return task; } /// <summary> /// 封裝Dispatcher.BeginInvoke /// 例:ThreadHelper.BeginInvoke(this.Dispatcher, () => { }, (ex) => { }); /// </summary> /// <param name="errorAction">錯誤處理</param> public static void BeginInvoke(Dispatcher dispatcher, Action action, Action<Exception> errorAction = null) { dispatcher.InvokeAsync(new Action(() => { try { DateTime dt = DateTime.Now; action(); double d = DateTime.Now.Subtract(dt).TotalSeconds; if (d > 0.01) LogUtil.Log("ThreadHelper.BeginInvoke UI耗時:" + d + "秒 " + action.Target.ToString()); } catch (Exception ex) { if (errorAction != null) errorAction(ex); LogUtil.LogError(ex); } }), DispatcherPriority.Background); } } }
測試方法:

private void Test23() { //變量定義 DateTime dt = DateTime.Now; Random rnd = new Random(); int taskCount = 1000; LimitedTaskScheduler scheduler = new LimitedTaskScheduler(); //生成測試數據 BlockingCollection<double> _data = new BlockingCollection<double>(); for (int i = 0; i < taskCount; i++) { _data.Add(rnd.NextDouble()); } //數據計算 Thread thread = new Thread(new ThreadStart(() => { dt = DateTime.Now; for (int i = 0; i < taskCount; i++) { ThreadHelper.Run(() => { Thread.Sleep(50); double a; if (_data.TryTake(out a)) { double r = Math.PI * a; } }, scheduler); } double d = DateTime.Now.Subtract(dt).TotalSeconds; this.BeginInvoke(new Action(() => { textBox1.Text += "調用" + taskCount + "次ThreadHelper.Run耗時:" + d.ToString() + "秒\r\n"; })); })); thread.IsBackground = true; thread.Start(); //數據計算耗時 Thread thread2 = new Thread(new ThreadStart(() => { while (_data.Count > 0) { Thread.Sleep(1); } double d = DateTime.Now.Subtract(dt).TotalSeconds; this.BeginInvoke(new Action(() => { textBox1.Text += "數據計算結束,耗時:" + d.ToString() + "秒\r\n"; })); })); thread2.IsBackground = true; thread2.Start(); scheduler.Dispose(); } private LimitedTaskScheduler _scheduler = new LimitedTaskScheduler(); private void Test24() { //點擊按鈕耗時 DateTime dt = DateTime.Now; ThreadHelper.Run(() => { double d = DateTime.Now.Subtract(dt).TotalSeconds; this.BeginInvoke(new Action(() => { textBox1.Text += "點擊按鈕耗時:" + d.ToString() + "秒\r\n"; })); }, _scheduler); }
事件方法:

private void button1_Click(object sender, EventArgs e) { Test23(); } private void button2_Click(object sender, EventArgs e) { Test24(); }
測試操作步驟:
依次點擊3次button1和1次button2
使用Run測試結果:
使用Run2測試結果:
結論:使用Run,點擊button2時,卡了好幾秒才出來結果,而使用Run2,點擊button2時,立即顯示結果,button2的操作本身應該耗時極少。
現實意義:當一批耗時任務無腦使用Task.Factory.StartNew時,另一個使用Task.Factory.StartNew的任務就無法及時響應了。