从版本5.0开始,C#引入了async/await的异步编程模式,大大简化了异步编程的难度,使我们摆脱了回调函数、事件等传统的方式,可以使用类似同步编程的方式进行异步编程,这里通过一个小例子说明这种编程方式以及需要注意的地方。
异步编程的主要目的是当执行到耗时较多的过程时,先启动这个过程,然后继续执行后续代码,当需要这个过程的返回的值时,再进行等待。这些耗时的过程可能是从网上下载文件,或者从数据库读入初始化数据等等。使用async/await方式编程时,需要使用async声明需要异步执行的过程,比如:
1 2 3 4 5 6 7 8 9
| async static Task<string> DoTask(int i) { Console.WriteLine("任务" + i+ "begin:" + Thread.CurrentThread.ManagedThreadId); await Task.Run(() => Console.WriteLine("任务" + i+ ":" + Thread.CurrentThread.ManagedThreadId)); val = i.ToString(); Console.WriteLine("任务" +i+ "end:" + Thread.CurrentThread.ManagedThreadId); return "任务"+i;
}
|
async返回的类型是Task或者Task<>,如果没有返回,可以是void。需要注意的是:async声明的函数或者方法中,必须有使用await语句执行的异步代码,否则这个函数或过程会被同步执行。反过来,使用await语句调用异步执行代码的函数,也必须声明为async。在同步函数中可以直接调用异步函数,使用Task.Result获取结果值,比如:
1
| string res=DoTask(10).Result;
|
下面的代码启动若干异步任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks;
namespace ZL.AsyncDemo { class Program { static string val; static void Main(string[] args) { Run(); Console.ReadLine(); } async static void Run() { var tasks = new List<Task<string>>(); for(var i = 0; i < 5; i++) { var task = DoTask(i); tasks.Add(task); }
Console.WriteLine("任务Main:" + Thread.CurrentThread.ManagedThreadId);
while (tasks.Any()) { Task<string> finished = await Task<string>.WhenAny(tasks); Console.WriteLine(finished.Result); tasks.Remove(finished); } Console.WriteLine(val); }
async static Task<string> DoTask(int i) { Console.WriteLine("任务" + i+ "begin:" + Thread.CurrentThread.ManagedThreadId); await Task.Run(() => Console.WriteLine("任务" + i+ ":" + Thread.CurrentThread.ManagedThreadId)); val = i.ToString(); Console.WriteLine("任务" +i+ "end:" + Thread.CurrentThread.ManagedThreadId); return "任务"+i;
} } }
|
从执行结果看,这些任务不是在同一线程执行的:
异步编程下需要注意的是访问公用变量的问题,如果多个异步任务对同一公用变量进行修改的化,结果可能不可预测,我们在这里定义了一个简单的公用变量,在异步任务中对它进行赋值,最后的结果是不可预测的,上次执行时结果是4,这次执行就变成了2: