下面的代码来自https://blog.stephencleary.com/2012/02/async-and-await.html,我刚刚添加了一些描述性方法
class Program
{
async static Task Main(string[] args)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); //thread id is 1 here
await DoSomethingAsync();
Console.WriteLine("do other workzzz");
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); //thread id is 4 here
Console.ReadLine();
}
public static async Task DoSomethingAsync()
{
await Task.Delay(2000);
}
}
作者说:
我喜欢将“等待”视为“异步等待”。也就是说,async方法会暂停,直到awaitable完成(因此它会等待),但是实际线程不会被阻塞(因此它是异步的)。
所以我的问题是:
为什么实际的线程(在我的情况下为线程ID 1)没有被阻塞?
在我看来,线程被阻塞是因为在当前方法完成之前不会执行其他语句。我们可以看到Console.WriteLine("do other workzzz");
在DoSomethingAsync()完成之前不会执行,不是一种阻塞吗?
要注意的另一重要事项是,DoSomethingAsync();
完成后,线程ID从1更改为4,不再有“实际线程”。为什么线程1消失了?是不是线程4消失了?
答案 0 :(得分:1)
(最初的问题是:“等待异步方法而不是让它并行运行时,您将其称为什么?”
我将使用“连续”这个词。他们一次一次运行。但是由于计划和执行的方式,该类型的任务(async
)仍然是异步的。不仅仅是调用者来决定它是否异步。 (此外,调用方为async
,其调用方也为async
,因此您实际上无法同步调用异步任务。(如果尝试使用Wait()
之类的函数,您可能会陷入僵局。因为在后台,它与同步功能不同。)
答案 1 :(得分:0)
我认为B部分缺少变量分配会使您感到困惑。
B部分可以这样重写:
var a = Task.Delay(1000);
await a;
var b = Task.Delay(1000);
await b;
var c = Task.Delay(1000);
await c;
etc...
如果您从头开始调用所有任务,然后在末尾等待所有任务(如A部分所示),则可以将其视为并行运行的那些任务。
await
表示要继续执行任务(在本例中为Task.Delay(1000)
)。
如果没有await
,您可以认为任务是在后台运行的(但是主程序可以在完成之前退出)
要等到所有任务完成后再退出程序,可以执行以下操作:
await Task.WhenAll(taskA, taskB, …);
答案 2 :(得分:0)
异步操作的处理方式与其他方法不同。
简化的解释:您的代码。
async static Task Main(string[] args) { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); //thread id is 1 here await DoSomethingAsync(); Console.WriteLine("do other workzzz"); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); //thread id is 4 here Console.ReadLine(); }
执行如下:
调用线程执行。.
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
由await
语句等待的代码被分派到任务调度程序,该任务调度程序使用同步上下文来对其进行排队,调度和执行。同步上下文的实现负责确定此代码在哪个线程中执行。
await DoSomethingAsync();
await
之后的代码被发布回开始时捕获的同步上下文。同样,根据实现,其他一些线程可能会执行它。
Console.WriteLine("do other workzzz");
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();
尽管代码是同步读取的,但是async/await
模式将破坏流程。
答案 3 :(得分:0)
为什么未阻止实际线程(在我的情况下为线程ID 1)?
在未完成的等待项上使用await
时,await
从async
方法返回。调用线程继续执行其下一段代码。
我们可以看到Console.WriteLine(“ do other workzzz”);在DoSomethingAsync()完成之前不会执行,不是一种阻塞吗?
它不会阻塞线程。该方法在await
点被“暂停”,但是没有线程将其固定在适当的位置。
例如,检查您的调用堆栈在await
之前,然后在await
之后。您会看到调用栈未被保留,因为返回了async
方法并且原始线程继续运行。在await
之后,不重新进入该调用堆栈。
另一个需要注意的重要事项是在DoSomethingAsync()之后;完成后,线程ID从1更改为4,不再有“实际线程”。为什么线程1消失了?是不是线程4消失了?
线程1和4均为实际线程。线程1是主线程,它是启动Console应用程序的主线程。线程4是线程池线程。
当DoSomethingAsync
完成并且await
准备好恢复其async
方法时,它必须在某个地方恢复它。默认情况下,await
将捕获“上下文”;对于控制台应用程序,这是线程池上下文。因此,async
方法恢复时,它在线程池线程上恢复。