程序只允许创建UI元素元素的线程操作UI上的元素,否正出现多线程访问异常。
C# 5.0 引入了新的语言特性,称为异步函数。它是TPL之上的更高级别的抽象,真正简化了异步编程。
创建一个异步函数,首先使用async关键字标注一个方法。异步函数必须返回Task或 Task类型,可以使用async void方法,但是更推荐使用async Task,使用async void方法唯一合理的地方再UI控制器事件处理时。方法内部必须至少使用一个await操作符,该操作可以与TPL的任务一起工作,并获取该任务操作的结果。
如果异步函数中有2个连续的await操作符,事实上这2个操作不会并行的,第2个操作在第1个操作完成后才开始运行的,顺序执行。
使用async和await存在一些限制:
1. 不能把控制台程序的Main方法标记为 async
2. 不能在 catch, finally, lock, unsafe, 代码块中使用await操作符
3. 不允许对任何异步方法使用 ref 或 out参数
使用async方法会有显著的性能损失。不需把每个方法都标注为异步方法。
(1) 使用await操作符获取异步任务结果
首先创建一个演示异步函数,创建一个2秒完成的任务,返回工作线程信息的字符串。
private async static Task GetInfoAsync(string name)
{
await Task.Delay(2000);
//throw new Exception(“Test Exception”);
return $“Task {name} is running on a thread id {Thread.CurrentThread.ManagedThreadId}.” +
$" Is thread pool:{Thread.CurrentThread.IsThreadPoolThread}";
}
再创建2个操作:一个是标准的TPL模式,一个是使用async,await的异步函数
static Task AsyncWithTPL()
{
Task t = GetInfoAsync(“Task 1”);
// 创建任务2,在任务1无异常时执行
Task t2 = t.ContinueWith(task => Console.WriteLine(t.Result), TaskContinuationOptions.NotOnFaulted);
// 创建任务3,在任务1异常时执行
Task t3 = t.ContinueWith(task => Console.WriteLine(t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted);
return Task.WhenAny(t2, t3);
}
测试:
无异常: //throw new Exception(“Test Exception”); //用于测试异常处理是否工作
Task t = AsyncWithTPL();
t.Wait();
输出:
Task Task 1 is running on a thread id 2. Is thread pool:True
使用异步函数测:
static async Task AsyncWithAwait()
{
try
{
string result = await GetInfoAsync(“Task 2”);
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
使用变得简单:
Task t = AsyncWithAwait();
t.Wait();
输出:
Task Task 2 is running on a thread id 4. Is thread pool:True
和编写同步代码风格一样了,使用await后,C#立即创建一个任务,执行一个后续操作任务,包含await操作符后面的所有剩余代码。该方法返回到主方法并等待其完成。但是在Windows GUI 或ASP.NET之类的环境总不推荐s使用Task.Wait,Task.Result,这会导致死锁。