概述
await/async 关键字组合是C#中异步实现的一种常用方式。
async
用于修饰方法,表明该方法为异步方法。但只有async+await
的组合才能真正实现异步的执行。若“异步方法”内没有await
,则该方法不是异步方法,仍为同步模式。- 由于
await
只能用于异步方法中,所以方法必须用async
修饰。 - 异步方法的放回类型必须为
void
或Task
或Task<TResult>
中的一种。 - 异步方法内必须含有
await
,用于表示异步等待的位置。虽然不太恰当,但有点类似debug中的断点。 await
后只能修饰Task
或Task<TResult>
。(方法的返回类型或变量类型)await task
的内容等价于task.Result
。await
关键字上下的代码有可能处于不同的线程。
说的挺清楚,就是示例代码命名有点长有点绕。
https://www.cnblogs.com/zhaoshujie/p/11192036.html
https://www.cnblogs.com/zhili/archive/2013/05/15/Csharp5asyncandawait.html
死锁
public class AsyncDemo
{
public async void Run()
{
Console.WriteLine($"Demo start\t{Thread.CurrentThread.ManagedThreadId}");
var task = GetAsync();
// await task; // 取消注释则无死锁
string ret = task.Result; // 死锁位置A
Console.WriteLine($"Demo end\t{Thread.CurrentThread.ManagedThreadId}");
}
public async Task<string> GetAsync()
{
Console.WriteLine($"GetAsync start\t{Thread.CurrentThread.ManagedThreadId}");
var task = await Task.Run(() =>
{
Console.WriteLine($"Task start\t{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(2000);
Console.WriteLine($"Task end\t{Thread.CurrentThread.ManagedThreadId}");
return "xxxx";
});// 死锁位置B
Console.WriteLine($"GetAsync end\t{Thread.CurrentThread.ManagedThreadId}");
return task;
}
}
死锁状态
取消注释后
死锁的原因在于,两个死锁位置的代码均锁定了共享资源,即阻塞了当前的context
。从线程id也可以看出,死锁的时候,GetAsync
无法进入GetAsync end 1
那一行,因为线程1正被阻塞在位置A。导致task的结果没办法返回线程1,处于等待中,形成了位置B。
注意,如果string ret = task.Result;
代码不存在,即不使用task的结果,则两种情况都不会死锁。因为没有出现抢占共享资源的情况。
https://www.cnblogs.com/OpenCoder/p/4434574.html 死锁
https://blog.stephencleary.com/2012/02/async-and-await.html
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html