异步函数是C# 5.0引入的新语言特性。使用异步函数可以规避人为线程操作,简化了异步编程的方法。
关键词 Async、Await
使用关键词async 、await创建异步函数,首先使用async标记异步函数,返回结果是Task、Task< T > 、void。异步函数内需要用await标记函数内异步操作。
异步函数的调用过程:
- 在线程池中发起await标记的异步操作,返回工作线程;
- 工作线程继续执行后续操作,直至await标记的异步操作完成;
- 当异步操作完成后,执行异步函数中后续操作。
限制条件:
- 控制台程序的Main函数不能标记为async
- catch、finally、unsafe、lock代码块中不能使用await操作符
注意:
- 通过async调用方法比正常调用方法慢40~50倍,对性能有一定影响,故需在必要时使用;
- 如果在工作线程中直接调用异步函数结果,则会阻塞当前线程,直至异步函数完成;
- 在不需要捕获线程上下文中,可使用ConfigureAwait,避免由于切换上下文带来的额外开销。
class Program
{
static void Main(string[] args)
{
var task = GetAsyncWithAwait();
Console.WriteLine("In Main");
task.Wait();
Console.ReadKey();
}
public async static Task GetAsyncWithAwait()
{
//显式指定 lambda表达式返回类型
//多个await 顺序执行
Func<string,Task<string>> t1 = async o=>
{
await Task.Delay(10);
Console.WriteLine("Wait 10s");
return o;
};
var t2 = await AsyncTest();
var str = await t1(t2);
Console.WriteLine(str);
}
public async static Task<string> AsyncTest()
{
await Task.Delay(100);
return "AsyncTest Is Done";
}
}
捕获异常
当await标记一个及一个以上异步操作时,使用Exception.Flatten()将层级异常存入列表中。
public static async Task CatchException()
{
var t1 = TaskOne();
var t2 = TaskTwo();
var task = Task.WhenAll(t1, t2);
try
{
await task;
}
catch
{
var ae = task.Exception.Flatten();
var ex = ae.InnerExceptions;
foreach (var item in ex)
{
Console.WriteLine($"{item.Message}");
}
}
}
public async static Task TaskOne()
{
await Task.Delay(10);
throw new Exception($"TaskOne is Done, CurrentThreadId is {Thread.CurrentThread.ManagedThreadId}");
}
public async static Task TaskTwo()
{
await Task.Delay(100);
throw new Exception($"TaskTwo is Done, CurrentThreadId is {Thread.CurrentThread.ManagedThreadId}");
}