LimitedTaskScheduler:
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Utils { public class LimitedTaskScheduler : TaskScheduler, IDisposable { [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")] public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize); private BlockingCollection<Task> _tasks = new BlockingCollection<Task>(); private int _timeOut = Timeout.Infinite; public LimitedTaskScheduler() { for (int i = 0; i < 10; i++) { Thread thread = new Thread(new ThreadStart(() => { Task task; while (_tasks.TryTake(out task, _timeOut)) { TryExecuteTask(task); } })); thread.IsBackground = true; thread.Start(); } } protected override IEnumerable<Task> GetScheduledTasks() { return _tasks; } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return true; } protected override void QueueTask(Task task) { _tasks.Add(task); } public void Dispose() { _timeOut = 1000; Thread thread = new Thread(new ThreadStart(() => { Thread.Sleep(1000); GC.Collect(); GC.WaitForPendingFinalizers(); if (Environment.OSVersion.Platform == PlatformID.Win32NT) { SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1); } })); thread.IsBackground = true; thread.Start(); } } }
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的任务就无法及时响应了。