异步/等待同步运行?

时间:2019-07-09 05:29:29

标签: c# .net async-await

下面的代码来自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消失了?

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方法恢复时,它在线程池线程上恢复。