C#如何Task执行任务,等待任务完成
作者:熊思雨
这篇文章主要介绍了C#如何Task执行任务,等待任务完成,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
Task执行任务,等待任务完成
代码:
//任务 Func<int> Funcs = () => { Console.WriteLine("任务开始"); return 1 + 1; }; //执行任务 Task<int> printRes = Task.Run(Funcs); //等待任务完成 printRes.GetAwaiter().OnCompleted(() => { Console.WriteLine("异步执行结果:" + printRes.Result); });
运行:
任务开始
异步执行结果:2
C# Task任务队列
需求
众所周知,方法体内代码是从上往下执行的,在我们工作中经常会遇到一些需要延时执行,但又必须按顺序来执行的需求。这要怎么解决呢。微软官方提供的Task API就是专门来解决这个问题的。那么下面就开始吧。
基本的Task用法
新建一个Winfrom项目
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace 线程2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { Task task1 = new Task(() => { Thread.Sleep(400); Console.WriteLine("task1"); }); Task task2 = new Task(() => { Thread.Sleep(300); Console.WriteLine("task2"); }); Task task3 = new Task(() => { Thread.Sleep(200); Console.WriteLine("task3"); }); Task task4 = new Task(() => { Thread.Sleep(100); Console.WriteLine("task4"); }); task1.Start(); task2.Start(); task3.Start(); task4.Start(); } } }
运行:
由于各个任务内部延时不同,最先执行的Task1,反而最后一个执行完,如果既要做延时操作,又要求从任务按顺序执行,要怎么解决呢?
让Task任务按顺序执行
修改代码:
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace 线程2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private List<Task> TaskList = new List<Task>(); private void Form1_Load(object sender, EventArgs e) { Task task1 = new Task(() => { Thread.Sleep(400); Console.WriteLine("task1"); }); Task task2 = new Task(() => { Thread.Sleep(300); Console.WriteLine("task2"); }); Task task3 = new Task(() => { Thread.Sleep(200); Console.WriteLine("task3"); }); Task task4 = new Task(() => { Thread.Sleep(100); Console.WriteLine("task4"); }); TaskList.Add(task1); TaskList.Add(task2); TaskList.Add(task3); TaskList.Add(task4); foreach (Task task in TaskList) { task.Start(); task.Wait(); } } } }
运行:
用上面的方法虽然有效,你可以看看,点击界面的时候,界面处鼠标指针会一直转圈,导致winfrom界面卡住,无法操作,这是因为使用Thread.Sleep 导致主线程阻塞,下面就来解决UI界面卡死的问题。
使用异步委托解决UI界面卡死问题
代码:
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace 线程2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private List<Task> TaskList = new List<Task>(); private void Button_Calculate_Click(object sender, EventArgs e) { Task task1 = new Task(async () => { await Task.Delay(TimeSpan.FromSeconds(4)); Console.WriteLine("task1"); }); Task task2 = new Task(async () => { await Task.Delay(TimeSpan.FromSeconds(3)); Console.WriteLine("task2"); }); Task task3 = new Task(async () => { await Task.Delay(TimeSpan.FromSeconds(2)); Console.WriteLine("task3"); }); Task task4 = new Task(async () => { await Task.Delay(TimeSpan.FromSeconds(1)); Console.WriteLine("task4"); }); TaskList.Add(task1); TaskList.Add(task2); TaskList.Add(task3); TaskList.Add(task4); foreach (Task task in TaskList) { task.Start(); task.Wait(); } } } }
运行:
用异步方式虽然界面不会卡住,但另一个问题来了,task.wait()方法似乎没有效果。里面的任务队列依然没有按顺序来执行。那么如何即使用异步执行,也不阻塞主线程,而且要任务按顺序来执行呢?
异步任务队列按顺序执行
代码:
private void Test() { Task.Run(() => { Task t1 = new Task(() => { Thread.Sleep(2000); Console.WriteLine("t1"); num = 1; }); t1.Start(); t1.Wait(); Task t2 = new Task(() => { Thread.Sleep(1000); Console.WriteLine("t2"); num = 3; }); t2.Start(); t2.Wait(); Console.WriteLine("线程执行完毕"); }); }
运行:
效果是实现了,代码看起来好搓啊,强迫症都犯了,没关系,可以使用更优雅的写法:
private async void Test() { await Task.Run(async () => { await Task.Delay(4000); Trace.WriteLine("第1个线程执行"); }); await Task.Run(async () => { await Task.Delay(3000); Trace.WriteLine("第2个线程执行"); }); await Task.Run(async () => { await Task.Delay(2000); Trace.WriteLine("第3个线程执行"); }); }
运行:
到此为止,功能就实现了,这个需求在Unity3d中使用协程很简单的几句就可以搞定,但在Winfrom等项目的开发中,确实有点繁琐。
封装任务队列
下面的代码我不认为是一个很好的写法,需要添加任务后,还得手动去调用,如果能添加到任务队列就不管了,让其自己自动按顺序来执行任务,岂不是更好,读者如果有兴趣自己去完善这个猜想。另外,在游戏开发中,比如RGP项目中,有专门的任务系统,它和我这个帖子的概念不能混为一谈,RPG任务系统更多的偏向数据的存取,来获取任务的完成状态。
using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Utils { public class TaskQueue { /// <summary> /// 任务列表 /// </summary> private List<Task> TaskList = null; /// <summary> /// 是否在执行任务中 /// </summary> private bool isPerformTask = false; /// <summary> /// 执行完任务的回调 /// </summary> public Action CallBack = null; private static TaskQueue _instance = null; public static TaskQueue Instance { get { if (_instance == null) _instance = new TaskQueue(); return _instance; } } /// <summary> /// 添加任务 /// </summary> /// <param name="task"></param> public void AddTask(Task task) { if (isPerformTask) { Console.WriteLine("[TaskQueue]任务正在执行中,此时不能做赋值操作"); return; } if (task != null) { TaskList.Add(task); } } /// <summary> /// 执行任务 /// </summary> public void PerformTask() { if (isPerformTask) { Console.WriteLine("[TaskQueue]任务正在执行中,不可重复调用"); return; } if (TaskList == null || TaskList.Count == 0) { Console.WriteLine("[TaskQueue]任务列表为空"); return; } Task.Run(() => { isPerformTask = true; foreach (Task item in TaskList) { item.Start(); item.Wait(); } TaskList.Clear(); isPerformTask = false; if (CallBack != null) CallBack(); }); } private TaskQueue() { TaskList = new List<Task>(); } } }
调用:
Task task1 = new Task(() => { Thread.Sleep(1000); Console.WriteLine("t1"); }); Task task2 = new Task(() => { Thread.Sleep(2000); Console.WriteLine("t2"); }); Task task3 = new Task(() => { Console.WriteLine("t3"); }); Action callback = () => { Console.WriteLine("所有任务执行完成"); }; TaskQueue.Instance.AddTask(task1); TaskQueue.Instance.AddTask(task2); TaskQueue.Instance.AddTask(task3); TaskQueue.Instance.CallBack = callback; TaskQueue.Instance.PerformTask();
运行:
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。