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

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);
    }
}

据我所知,异步编程是并发编程的一种形式,它允许两段独立的代码同时运行.但所有这些等待操作符都会使代码一致运行,实际上,我们可以使用以下示例,它会给出相同的输出:

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);
    }
}

那么,当等待操作符实际上使异步代码同步时,我为什么要使用它们呢?

推荐答案

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

说大也大吧.这就是我所说的"假异步":代码使用await Task.Run来异步等待线程池线程完成的同步工作.这与"真正的异步"不同,"真正的异步"通常是I/O(即没有线程池线程等待操作完成).然而,"假的"异步并不意味着它是bad;它非常适合UI应用程序.

据我所知,异步编程是并发编程的一种形式,它允许两段独立的代码同时运行.

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

关键问题是,异步不会阻塞调用线程.它的目的是到free up threads.

因此,回到"假异步":从一个Angular 来看,是的,它是异步的,因为它不会阻塞调用线程.然而,我称它为"假异步",因为它阻塞线程池线程以释放调用线程.同样,在某些情况下,这是非常好的,例如,如果调用线程是一个UI线程,并且您想让该UI保持响应.

但所有这些等待运算符使代码运行一致...那么,当等待操作符实际上使异步代码同步时,我为什么要使用它们呢?

我相信您要找的术语是"Serial",它经常与"Synchronous"混淆.串行码一次运行一个.在您发布的示例中,使用了一个非常常见的模式:

var count = await Method1();

因此,Method1被启动并返回(这是异步代码,因此Method1返回它完成的Task before;当Method1完成时,已经返回的Task将被完成).则调用线程立即执行await.

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

然而,"串口"并不意味着"同步".所有同步代码本质上都是串行的(您必须使用并行度来强制并发性).most个异步码是also串行码.但是,它是asynchronously系列的;调用线程不会被阻塞.在您的示例中,Main在到达其await时将返回一个不完整的Task,并且该线程被释放并且不会被Main阻塞.

在控制台应用程序中很难看到这种好处.但如果线程是UI线程,则您的UI可以保持响应.如果线程是ASP.NET线程池线程,则它可以处理其他请求,从而提高服务器的可伸缩性(请注意,只有ASP.NET上的"假异步"才是"真异步").

Csharp相关问答推荐

在包含空项的列表上使用具有断言T的摘要表

C++/C#HostFXR通过std::tuple传递参数

为什么在ANTLR4中会出现不匹配的输入错误?""

AsNoTrackingWithIdentitySolutions()似乎不起作用?

.NET HttpClient、JsonSerializer或误用的Stream中的内存泄漏?

不带身份的Blazor服务器.Net 8 Cookie身份验证

有没有办法使.NET 6应用程序在特定的.NET 6运行时版本上运行

如何测量在使用UTF8而不是C#中的UTF16编码字符串时内存使用量的增长

Azure函数中实体框架核心的依赖注入

当用户右键单击文本框并单击粘贴时触发什么事件?

如何让NLog停止写入冗余信息?

当试图限制EF Select 的列时,如何避免重复代码?

如何从Azure函数使用Graph API(SDK 5.35)中的[FindMeetingTimes]

如何在使用属性 Select 器时判断是否可以为空

如何正确处置所有动态控件?

如何在Polly重试策略成功之前将HttpClient请求排队?

如何使用实体框架核心对字符串_agg使用强制转换为varchar(Max)

使用Blazor WebAssembly提高初始页面加载时间的性能

将字符串类型日期输入(yyyy-mm-ddthh:mm:ss)转换为MM/dd/yyyy格式

无效的Zip文件-Zip存档