wait 会使异步代码同步吗?

yeg*_*gor 2 c# asynchronous async-await

我正在学习异步和等待在编程中如何工作。我在互联网上看到这个例子,说这是异步代码:

class Program
{
    static async Task Main(string[] args)
    {
        Method2();
        var count = await Method1();
        Method3(count);
        Console.ReadKey();
    }

    static async Task<int> Method1()
    {
        int count = 0;
        await Task.Run(() =>
        {
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine(" Method 1");
                count += 1;
            }
        });
        return count;
    }

    static void Method2()
    {
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine(" Method 2");
        }
    }

    static void Method3(int count)
    {
        Console.WriteLine("Total count is " + count);
    }
}
Run Code Online (Sandbox Code Playgroud)

据我了解,异步编程是并发编程的一种形式,它允许两个单独的代码同时运行。但所有这些等待运算符使代码运行一致,实际上,我们可以使用以下示例,它给出相同的输出:

class Program
{
    static async Task Main(string[] args)
    {
        Method2();
        Method3(Method1());
        Console.ReadKey();
    }

    static int Method1()
    {
        int count = 0;
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine(" Method 1");
            count += 1;
        }
        return count;
    }

    static void Method2()
    {
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine(" Method 2");
        }
    }

    static void Method3(int count)
    {
        Console.WriteLine("Total count is " + count);
    }
}
Run Code Online (Sandbox Code Playgroud)

那么,为什么我要使用await 运算符,因为它们本质上使异步代码同步呢?

Ste*_*ary 5

我在互联网上看到这个例子,说这是异步代码

有点。这就是我所说的“假异步”:代码用于await Task.Run异步等待线程池线程完成的同步工作。这与“真正的异步”形成对比,“真正的异步”通常是 I/O(即,没有线程池线程等待操作完成)。不过,“假”异步并不意味着它是坏的;它非常适合 UI 应用程序。

据我了解,异步编程是并发编程的一种形式,它允许两个单独的代码同时运行。

我想说并发编程是同时运行单独的代码片段。然后有两种不同类型的并发编程:并行和异步。并行性使用多个线程并发运行;异步使用非阻塞方法来并发运行。

关键要点是异步不会阻塞调用线程。其目的是释放线程

所以,回到“假异步”:从一个角度来看,是的,它是异步的,因为它不会阻塞调用线程。但我称其为“假异步”,因为它会阻塞线程池线程以释放调用线程。同样,在某些情况下这完全没问题,例如,如果调用线程是 UI 线程并且您希望保持 UI 响应。

但是所有这些await 运算符都使代码运行一致......那么,当await 运算符本质上使异步代码同步时,为什么我要使用await 运算符呢?

我相信您正在寻找的术语是“串行”,它经常与“同步”混淆。串行代码一次运行一个。在您发布的示例中,使用了一种非常常见的模式:

var count = await Method1();
Run Code Online (Sandbox Code Playgroud)

因此,Method1启动并返回(这是异步代码,因此在完成之前Method1返回 a ;已返回的将在完成时完成)。然后调用线程立即执行.Task TaskMethod1await

这是一种非常常见的模式,用于确保进度被序列化:我们不想Main继续执行,直到Method1完成并且我们可以将结果放入count.

然而,“串行”并不意味着“同步”。所有同步代码本质上都是串行的(您必须使用并行性来施加并发性)。大多数异步代码也是串行。然而,它是异步串行的;调用线程没有被阻塞。在您的示例中,当到达其 时,将返回不完整,并且该线程被释放并且不会被 阻塞。MainTaskawaitMain

在控制台应用程序中很难看到这种好处。但如果该线程是 UI 线程,那么您的 UI 可以保持响应。如果该线程是 ASP.NET 线程池线程,那么它可以处理其他请求,从而提高服务器的可伸缩性(请注意,对于 ASP.NET 上的“假异步”而言,情况并非如此,只有“真异步”)。