Task执行多个任务并取得汇总结果(不卡界面)
如何执行多个Task任务的汇总结果,并且不会卡界面的方法:
解决步骤:
1、单个且有返回值的Task任务,将结果显示到界面上,分析卡界面的原因;
2、单个且有返回值的Task任务,将结果显示到界面上,不卡界面;
3、多个且有返回值的Task任务集合,将结果汇总后显示到界面上,不卡界面;
按下按钮开始计算一个耗时的加法,并将加法结果返回显示到界面上。如下:
1、单个且有返回值的Task任务,将结果显示到界面上,分析卡界面的原因;
private void button1_Click(object sender, EventArgs e) { Task<int> t = new Task<int>(() => GetSum(2, 3)); t.Start(); label1.Text = "结果:" + t.Result.ToString(); } int GetSum(int v1, int v2) { Thread.Sleep(2000); return v1 + v2; }
分析:卡界面的原因是发生在获取线程结果时,即:t.Result这里需要等,而不是t.Start()。
2、单个且有返回值的Task任务,将结果显示到界面上,不卡界面;
分析1中的原因,因为t.Result这里需要等待,导致界面卡,所以可以将等结果的这一步放到一个新线程中等。如下:
//不卡界面,将获取结果的耗时操作放入另一线程中等待 private void button1_Click(object sender, EventArgs e) { Task<int> t = new Task<int>(() => GetSum(3, 3)); t.Start(); Task.Run(() => { int result = t.Result; this.Invoke(new Action(() => { //label1.Text = "结果:" + t.Result.ToString();//这种同样卡界面,相当于委托主线程去等结果 label1.Text = "结果:" + result;//不卡界面,获取结果在子线程中等,这里只是更新UI })); }); }
3、多个且有返回值的Task任务集合,将结果汇总后显示到界面上,不卡界面;
通过上面的方法,多个Task任务集合与单个Task任务获得汇总结果并显示的方法是相似的,如下:
//等待多个线程的结果,并显示,第一种方法 private void button1_Click(object sender, EventArgs e) { List<Task<int>> taskList = new List<Task<int>>(); Task<int> t1 = new Task<int>(() => GetSum(1, 3)); Task<int> t2 = new Task<int>(() => GetSum(2, 3)); Task<int> t3 = new Task<int>(() => GetSum(3, 3)); taskList.Add(t1); taskList.Add(t2); taskList.Add(t3); taskList.ForEach((task) => task.Start());//全部启动线程 Task.Run(() => { int sum = 0; foreach (var item in taskList) { sum += item.Result; } this.Invoke(new Action(() => { label1.Text = sum.ToString(); })); }); } int GetSum(int v1, int v2) { Thread.Sleep(5000); return v1 + v2; }
4、采用异步,多个且有返回值的Task任务集合,将结果汇总后显示到界面上,不卡界面;参考:异步多线程解决界面卡顿问题 - hanzq_go - 博客园 (cnblogs.com)
//等待多个线程的结果,并显示,第二种方法 private async void button1_Click(object sender, EventArgs e) { List<Task<int>> taskList = new List<Task<int>>(); Task<int> t1 = new Task<int>(() => GetSum(1, 3)); Task<int> t2 = new Task<int>(() => GetSum(2, 3)); Task<int> t3 = new Task<int>(() => GetSum(3, 3)); taskList.Add(t1); taskList.Add(t2); taskList.Add(t3); taskList.ForEach((task) => task.Start());//全部启动线程 int[] resultList = await Task.WhenAll(taskList.ToArray()); label1.Text = resultList.Sum().ToString(); } int GetSum(int v1, int v2) { Thread.Sleep(5000); return v1 + v2; }
总结:
所以想不卡界面,就不要在主线程等待r.result。可以新建一个线程执行等待结果操作。
除非方法本身是异步方法,如whenAll(),它可以使用await关键字直接在发起线程等待,不会阻塞界面。
另外:Task.When All和Task.Wait All的区别
Task.WaitAll();无返回值。
Task.WhenAll();有返回值。可以直接拿到任务完成后的数据或者取得任务运行状态。