关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

.NET 异步详解

发布时间:2020-08-06 10:46:31

初看异步#

说到异步大家应该都很熟悉了,2012 年 C# 5 引入了新的异步机制:Task,并且还有两个新的关键字 await 和 async,这已经不是什么新鲜事了,而且如今这个异步机制已经被各大语言借鉴,如 JavaScript、TypeScript、Rust、C++ 等等。

下面给出一个简单的对照:

语言

调度单位

关键字/方法

C#

Task<>ValueTask<>

asyncawait

C++

std::future<>

co_await

Rust

std::future::Future<>

.await

JavaScript、TypeScript

Promise<>

asyncawait

当然,这里这并不是本文的重点,只是提一下,方便大家在有其他语言经验的情况下(如果有),可以认识到 C# 中 Task 和 async/await 究竟是一个和什么可以相提并论的东西。

多线程编程#

在该异步编程模型诞生之前,多线程编程模型是很多人所熟知的。一般来说,开发者会使用 Threadstd::thread 之类的东西作为线程的调度单位来进行多线程开发,每一个这样的结构表示一个对等线程,线程之间采用互斥或者信号量等方式进行同步。

多线程对于科学计算速度提升等方面效果显著,但是对于 IO 负荷的任务,例如从读取文件或者 TCP 流,大多数方案只是分配一个线程进行读取,读取过程中阻塞该线程:

Copy

void Main(){

    while (true)

    {

        var client = socket.Accept();

        new Thread(() => ClientThread(client)).Start();

    }

}

void ClientThread(Socket client){

    var buffer = new byte[1024];

    while (...)

    {

        // read and block

        client.Read(buffer, 0, 1024);

    }

}

上述代码中,Main 函数在接收客户端之后即分配了一个新的用户线程用于处理该客户端,从客户端接收数据。client.Read() 执行后,该线程即被阻塞,即使阻塞期间该线程没有任何的操作,该用户线程也不会被释放,并被操作系统不断轮转调度,这显然浪费了资源。

另外,如果线程数量多起来,频繁在不同线程之间轮转切换上下文,线程的上下文也不小,会浪费掉大量的性能。

异步编程#

因此对于此工作内容(IO),我们在 Linux 上有了 epoll/io_uring 技术,在 Windows 上有了 IOCP 技术用以实现异步 IO 操作。

(这里插句题外话,吐槽一句,Linux 终于知道从 Windows 抄作业了。先前的 epoll 对比 IOCP 简直不能打,被 IOCP 全面打压,io_uring 出来了才好不容易能追上 IOCP,不过 IOCP 从 Windows Vista 时代开始每一代都有很大的优化,io_uring 能不能追得上还有待商榷)

这类 API 有一个共同的特性就是,在操作 IO 的时候,调用方控制权被让出,等待 IO 操作完成之后恢复先前的上下文,重新被调度继续运行。

所以表现就是这样的:

假设我现在需要从某设备中读取 1024 个字节长度的数据,于是我们将缓冲区的地址和内容长度等信息封装好传递给操作系统之后我们就不管了,读取什么的让操作系统去做就好了。

操作系统在内核态下利用 DMA 等方式将数据读取了 1024 个字节并写入到我们先前的 buffer 地址下,然后切换到用户态将从我们先前让出控制权的位置,对其进行调度使其继续执行。

你可以发现这么一来,在读取数据期间就没有任何的线程被阻塞,也不存在被频繁调度和切换上下文的情况,只有当 IO 操作完成之后才会被重新调度并恢复先前让出控制权时的上下文,使得后面的代码继续执行。

当然,这里说的是操作系统的异步 IO 实现方式,以便于读者对异步这个行为本身进行理解,和 .NET 中的异步还是有区别,Task 本身和操作系统也没什么关系。

Task (ValueTask)#

说了这么久还是没有解释 Task 到底是个什么东西,从上面的分析就可以得出,Task 其实就是一个所谓的调度单位,每个异步任务被封装为一个 Task 在 CLR 中被调度,而 Task 本身会运行在 CLR 中的预先分配好的线程池中。

总有很多人因为 Task 借助线程池执行而把 Task 归结为多线程模型,这是完全错误的。

这个时候有人跳出来了,说:你看下面这个代码

Copy

static async Task Main(){

    while (true)

    {

        Console.WriteLine(Environment.CurrentManagedThreadId);

        await Task.Delay(1000);

    }

}

输出的线程 ID 不一样欸,你骗人,这明明就是多线程!对于这种言论,我也只能说这些人从原理上理解的就是错误的。

当代码执行到 await 的时候,此时当前的控制权就已经被让出了,当前线程并没有在阻塞地等待延时结束;待 Task.Delay() 完毕后,CLR 从线程池当中挑起了一个先前分配好的已有的但是空闲的线程,将让出控制权前的上下文信息(寄存器值)恢复,使得该线程恰好可以从先前让出的位置继续执行下去。这个时候,可能挑到了先前让出前所在的那个线程,导致前后线程 ID 一致;也有可能挑到了另外一个和之前不一样的线程执行下面的代码,使得前后的线程 ID 不一致。在此过程中并没有任何的新线程被分配了出去。

当然,在 WPF 等地方,因为利用了 SynchronizationContext 对调度行为进行了控制,所以可以得到和上述不同的结论,和这个相关的还有 .ConfigureAwait() 的用法,但是这里不是本文重点,因此就不做展开。

但是上面和经典的多线程编程的那一套一样吗?不一样。

至于 ValueTask 是个什么玩意,官方发现,Task 由于本身是一个 class,在运行时如果频繁反复的分配和回收会给 GC 造成不小的压力,因此出了一个 ValueTask,这个东西是 struct,分配在栈上,这样的话就不会给 GC 造成压力了,减轻了开销。不过也正因为 ValueTask 是会在栈上分配的值类型结构,因此提供的功能也不如 Task 全面。



相关阅读

计算机网络安全常见的危险因素有哪些?什么是内容分发网络(CDN)?云南服务器搭建和数据备份,恢复云南云服务器的三大作用人工智能AI初认识云计算服务的6个优势云南服务器托管有哪些注意事项Tomcat的特点云计算的三个优点选取小程序服务商时的注意事项不同数据库的不同区别什么是VPS主机它的优势是什么?你知道5G的优点和缺点吗?云南三级分销商城开发的目的和对企业的价值云南网站优化的3个方面和网页的优化我们晋级拉!!!云南网站链接维护的具体方法云南网站应该如何做优化云南网站维护的主要内容云南云服务器和虚拟主机的操作区别云服务器部署和注意点云南服务网器托管应该选择怎样的机房云南虚拟建设网站主机的优点和缺点云服务器和物理服务器的区别云服务器有哪些优势C# List用法 List介绍C#和Java有什么不同PHP的优点和缺点智慧新餐饮和传统餐饮的区别云数据库对比传统数据库有哪些优点裸金属服务器是什么它的作用是什么白盒测试的特点js中添加scriptjs中[]、{}、()的用法和区别php 字符串的整型转换ipa如何安装到苹果手机邓白氏码是什么?iOS开发者账号到期续费教程在C#中有哪些引用类型和值类型小程序搭建时需要准备些什么云数据库对比传统数据库的优势什么样的企业适合SEMjs中的常见错误C#数据类型转换字符串与数值之间的转换C#的几种循环遍历方式物联网是什么它和互联网又有什么区别?云南网站建设时应该注意些什么云计算是什么?它能干些什么?展望未来5G会给我们的生活带来哪些影响C#常见的几种报错类别C#的学习流程有哪些dedecms 绑定二级域名的正确方法SEM是什么它和SEO之间是什么关系?云南新餐饮料模式是如何运行的?云南网站建设初期应该注意哪些问题云南网站建设中原创文章对网站起什么作用Javascript中如何中断forEach循环云南零售小程序前景怎么样?如何优化WEB应用数据库访问慢的问题?Javascript中用let和var声明变量的区别是什么redis 的主从复制和哨兵?递归和迭代有何区别?<input> 标签的 readonly 属性怎么用?云南网站建设中网页版商城如何保证网站安全云南企业官网如何推广云南企业网站建设为什么把文章类型的栏目排在前面云南餐饮小程序能带给餐饮业什么?云南网站建设需要注意什么地方云南云服务器配置如何选择合适的云南网站建设和网站设计有何区别?云南网站建设—企业官网的好处在哪?云南做小程序的优势在哪?为什么建议做小程序?网站为什么要配置ssl证书?网站为什么要做seo,做seo有什么好处?微信小程序_企业付款到零钱 API 开发如何利用缓存提高asp.net网站访问速度中小型企业如何选择服务器配置?如何优化中小型企业网站C#.net做网站后台 需要记录日志基于asp.net企业门户网站设计霓裳民族服装seo优化分析建议Javascript的函数封装认识及使用Ajax请求中data与后端的交互有哪几种方法JavaScript如何实现组合模式???SQL之子查询的基本用法有哪些?什么是静态测试、动态测试、黑盒测试、白盒测试、α测试 β测试?C# 引入委托的目的是什么c# 委托的本质是什么C# DataGridView添加新行的2个方法C#支付宝扫码支付代码完整版c# MVC 微信支付教程系列之扫码支付代码实例Redis基础通用操作指令有哪些?String、StringBuffer和StringBuilder的各自用法常见的一些Dos命令有哪些?如何在C#中复制一个Windows窗体类前端js中的运算符的种类,=、==与===的有何区别?网站前端怎么设置页面禁止转载?解决XSS脚本攻击恶意代码的方法你知道?javascript 之 apply()、call() 探索net core实现跨域
/template/Home/Zkeys/PC/Static