.NET Core多线程 (2) 异步 - 上
去年换工作时系统复习了一下.NET Core多线程相关专题,学习了一线码农老哥的《.NET 5多线程编程实战》课程,我将复习的知识进行了总结形成本专题。
本篇,我们来复习一下异步的相关知识点,预计阅读时间10分钟。
理解异步的本质
(1)异步是什么?
举个例子,在高峰期去餐厅吃饭,会先排队拿个小票,然后去逛一下玩玩,等到排到时会被通知就餐,这时再回到餐厅就可以点餐了。
同步示意图:

异步示意图:

(2)同步有什么弊端
时间片切换成本高!
- CPU密集型操作:编码解码、图形计算、正则表达式等
- IO密集型操作:和硬件打交道,和DB打交道等
- 线程太多的烦恼/代价:
- 新开Thread是有开销的(时间、空间)
- GC回收会冻结所有线程寻找引用根(gcroot)
程序有可能会卡死!
- Thread会和网络驱动程序打交道(外网络地址)
- ThreadPool中的WorkQueue任务(4000+)得不到处理
- 异步:async/await
(3)C#如何使用异步?
ThreadPool线程池分类:
workThread:
适用于CPU密集型,在WinDbg中标签为 ThreadPool Worker
IOThread:
适用于IO密集型,在WinDbg中标签为 ThreadPool Completion Port
HttpClient案例演示:在下面的代码中GetContentLengthAsync异步方法中的线程就用的IOThread,可以通过WinDbg验证。
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
GetContentLengthAsync("http://cnblogs.com"); Console.WriteLine($"主线程:{Environment.CurrentManagedThreadId}, 准备退出!");
Console.ReadLine();
} static async Task<int> GetContentLengthAsync(string url)
{
using (HttpClient client = new HttpClient())
{
var content = await client.GetStringAsync(url); Console.WriteLine($"当前线程:{Environment.CurrentManagedThreadId}, content={content.Length}"); return content.Length;
}
}
}
}
异步的底层:IO完成端口
(1)理解IO完成端口
异步的核心:callback机制
IO完成端口:这是一个Windows内核对象,我们常称之为IOCP。IOCP是一个异步I/O的Windows API,它可以高效地将I/O事件通知给应用程序,类似于Linux中的Epoll。因此,.NET Framework是基于IOCP来实现的异步,而.NET Core则增加了基于epoll来实现异步,因为它要支持跨平台而不只是Windows。SafeHandle:文件句柄、网络句柄...
核心步骤:
初始化时将SafeHandle、ThreadPool与IO完成端口进行绑定(比如:FileStream在Init时)
(主线程)创建IO完成端口:CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads)
(主线程)将消息塞到IO完成端口的Queue队列:PostQueuedCompletionStatus
(子线程)从IO完成端口的Queue队列中获取消息:GetQueuedCompletionStatu

(2)实现一个简单的IO完成端口
自定义一个IOCP类,代码如下:
public class IOCP
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetQueuedCompletionStatus(SafeFileHandle CompletionPort,
out uint lpNumberOfBytesTransferred, out IntPtr lpCompletionKey,
out IntPtr lpOverlapped, uint dwMilliseconds); [DllImport("Kernel32", CharSet = CharSet.Auto)]
public static extern bool PostQueuedCompletionStatus(SafeFileHandle CompletionPort, uint dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);
}
调用端代码如下:
// 1. 创建IO完成端口
var safehandle = IOCP.CreateIoCompletionPort(new IntPtr(-1), IntPtr.Zero, IntPtr.Zero, 1); var thread = new Thread(() =>
{
Console.WriteLine($"工作线程: {Environment.CurrentManagedThreadId} 开始获取数据...");
while (true)
{
// 3. get数据
IOCP.GetQueuedCompletionStatus(safehandle, out var ipn, out var ipc, out var lop, int.MaxValue); var receiveData = Convert.ToString(GCHandle.FromIntPtr(lop).Target); Console.WriteLine($"工作线程: {Environment.CurrentManagedThreadId} 获取数据成功!{receiveData}"); Thread.Sleep(1000);
}
}); thread.Start(); // 2. post 数据
var data = (IntPtr)GCHandle.Alloc("hello world"); IOCP.PostQueuedCompletionStatus(safehandle, 4096, IntPtr.Zero, data); Console.WriteLine($"主线程: {Environment.CurrentManagedThreadId} 塞入数据成功!"); Console.ReadLine();
加深对异步的理解
我们都知道 ContinueWith 主要起 延续任务的作用,写起来十分繁琐!.NET 4.5推出了语法糖async/await大大简化了异步编程的工作量。
下面展示使用ContinueWith 和 async/await 的两种方式的代码量:
/// <summary>
/// continutewith 的版本
/// </summary>
/// <returns></returns>
static Task<List<string>> GetContentListContinute()
{
var list = new List<string>(); SqlConnection connection = new SqlConnection("Server=LocalHost; Persist Security Info=False;Integrated Security=SSPI;Database= PostDB;"); var task = connection.OpenAsync().ContinueWith(t =>
{
SqlCommand command = new SqlCommand("select PostContent from Post", connection); return command.ExecuteReaderAsync().ContinueWith(t2 =>
{
var reader = t2.Result; return GetContent(reader, list).ContinueWith(t3 =>
{
return list;
});
}).Unwrap(); }).Unwrap(); return task;
} static Task<bool> GetContent(SqlDataReader reader, List<string> list)
{
return reader.ReadAsync().ContinueWith(t =>
{
var hasRow = t.Result; if (hasRow)
{
list.Add(reader.GetString(0)); //读取reader的值
GetContent(reader, list);
} return false;
});
} /// <summary>
/// await+async 的异步写法
/// </summary>
/// <returns></returns>
static async Task<List<string>> GetContentListAsync()
{
List<string> list = new List<string>(); SqlConnection connection = new SqlConnection("Server=LocalHost; Persist Security Info=False;Integrated Security=SSPI;Database= PostDB;"); await connection.OpenAsync(); SqlCommand command = new SqlCommand("select PostContent from Post", connection); var reader = command.ExecuteReader(); while (await reader.ReadAsync())
{
list.Add(reader.GetString(0));
} return list;
}
async/await语法糖的底层原理
从编译后的IL代码来看,async/await只是编译器提供的语法糖,它并不是一种新的异步模型,而只是一种简化异步代码编写的方式。
从反编译后的代码来看,对于async/await的方法编译器会新生成一个实现了IAsyncStateMachine接口的状态机类。
(1)IAsyncStateMachine接口定义:
public interface IAsyncStateMachine
{
void MoveNext();
void SetStateMachine(IAsyncStateMachine stateMachine);
}
(2)IAsyncStateMachine实现类的基本执行步骤
step1.初始化一个异步状态机machine
step2.初始化一个AsyncTaskMethodBuilder的实例,赋予machine.builder
step3.设置异步状态机的状态为-1,将类传入到状态机内部
step4.调用machine.builder的start方法
step5.返回machine.builder.Task

(3).NET提供异步方式的总结:
.NET 4.5开始提供的async/await,本质是.NET 4.0的Task + 状态机
.NET 4.0开始提供的Task,本质是.NET 3.5提供的Thread+ThreadPool+等待/取消等API操作
小结
本篇,我们复习了异步相关的基础知识,但由于内容太多,因此将其拆分为了两篇推文。下一篇,我们继续异步相关知识。
参考资料
一线码农,腾讯课堂《.NET 5多线程编程实战》
不明作者,《Task调度与await》

.NET Core多线程 (2) 异步 - 上的更多相关文章
- 从Nginx的Web请求处理机制中剖析多进程、多线程、异步IO
Nginx服务器web请求处理机制 从设计架构来说,Nginx服务器是与众不同的.不同之处一方面体现在它的模块化设计,另一方面,也是最重要的一方面,体现在它对客户端请求的处理机制上. Web服务器和客 ...
- c# 中的多线程和异步
前言: 1.异步和多线程有区别吗? 答案:多线程可以说是实现异步的一种方法方法,两者的共同目的:使主线程保持对用户操作的实时响应,如点击.拖拽.输入字符等.使主程序看起来实时都保持着等待用户响应的状态 ...
- 第一章 管理程序流(In .net4.5) 之 实现多线程和异步处理
1. 概述 本章主要讲解.net4.5如何实现多线程和异步处理的相关内容. 2. 主要内容 2.1 理解线程 ① 使用Thread类 public static class Program { ...
- Android Learning:多线程与异步消息处理机制
在最近学习Android项目源码的过程中,遇到了很多多线程以及异步消息处理的机制.由于之前对这块的知识只是浅尝辄止,并没有系统的理解.但是工程中反复出现让我意识到这个知识的重要性.所以我整理出这篇博客 ...
- Android多线程及异步处理问题
1.问题提出 1)为何需要多线程? 2)多线程如何实现? 3)多线程机制的核心是啥? 4)到底有多少种实现方式? 2.问题分析 1)究其为啥需要多线程的本质就是异步处理,直观一点说就是不要让用户感觉到 ...
- 初步谈谈 C# 多线程、异步编程与并发服务器
多线程与异步编程可以达到避免调用线程异步阻塞作用,但是两者还是有点不同. 多线程与异步编程的异同: 1.线程是cpu 调度资源和分配的基本单位,本质上是进程中的一段并发执行的代码. 2.线程编程的思维 ...
- [Xcode 实际操作]八、网络与多线程-(22)使用GCD多线程技术异步下载图片
目录:[Swift]Xcode实际操作 本文将演示如何使用使用GCD多线程技术异步下载图片. Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法 ...
- Task C# 多线程和异步模型 TPL模型 【C#】43. TPL基础——Task初步 22 C# 第十八章 TPL 并行编程 TPL 和传统 .NET 异步编程一 Task.Delay() 和 Thread.Sleep() 区别
Task C# 多线程和异步模型 TPL模型 Task,异步,多线程简单总结 1,如何把一个异步封装为Task异步 Task.Factory.FromAsync 对老的一些异步模型封装为Task ...
- C#基础之多线程与异步
1.基本概念 多线程与异步是两个不同概念,之所以把这两个放在一起学习,是因为这两者虽然有区别,但也有一定联系. 多线程是一个技术概念,相对于单线程而言,多线程是多个单线程同时处理逻辑.例如,假如说一个 ...
- C#多线程与异步
1.什么是异步同步 如果一个方法被调用,调用者需要等待该方法被执行完毕之后才能继续执行,则是同步. 如果方法被调用后立刻返回,即使该方法是一个耗时操作,也能立刻返回到调用者,调用者不需要等待该方法,则 ...
随机推荐
- Rsync文件同步及备份
Rsync文件同步及备份 目录 Rsync文件同步及备份 Rsync基本概述 远程文件传输 服务端口 Rsync的三种传输模式 本地方式(类似cp) 远程方式(类似scp) 守护进程(C/S结构) R ...
- 2023-03-20:给定一个无向图,保证所有节点连成一棵树,没有环, 给定一个正数n为节点数,所以节点编号为0~n-1,那么就一定有n-1条边, 每条边形式为{a, b, w},意思是a和b之间的无
2023-03-20:给定一个无向图,保证所有节点连成一棵树,没有环, 给定一个正数n为节点数,所以节点编号为0~n-1,那么就一定有n-1条边, 每条边形式为{a, b, w},意思是a和b之间的无 ...
- 2022-02-27:k8s安装yapi,yaml如何写?
2022-02-27:k8s安装yapi,yaml如何写? 答案2022-02-27: yaml如下: apiVersion: apps/v1 kind: Deployment metadata: l ...
- Hugging News #0512: 🤗 Transformers、🧨 Diffusers 更新,AI 游戏是下个新热点吗
每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...
- 【GiraKoo】could not find UI helper 'git-credential-manager-ui'
环境 Windows 11 git version 2.39.0.windows.1 TortoiseGit 现象 使用TortoiseGit执行git pull命令时,提示could not fin ...
- Java程序设计复习提纲(上:入门语法)
目录 上: 基本语法与编译运行 数据类型和关键字 常用语法 数组与字符串 异常处理 中: 面向对象和类 下: 图形界面 基本语法与编译运行 java没有指针没有全局变量 Java源代码文件的后缀名是& ...
- LeetCode 周赛 347(2023/05/28)二维空间上的 LIS 最长递增子序列问题
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 往期回顾:LeetCode 单周赛第 346 场 · 仅 68 人 AK 的最短路问题 周赛 347 概览 ...
- 文心一言 VS 讯飞星火 VS chatgpt (26)-- 算法导论5.1 1题
一.证明:假设在过程 HIRE-ASSISTANT 的第 4 行中,我们总能决定哪一个应聘者最佳.则意味着我们知道应聘者排名的全部次序. 文心一言: 证明: 假设在过程 HIRE-ASSISTANT ...
- 记录一些不知道哪里冒出来的 idea(可能已存在)
2022/7/4 给定一张 \(n\) 个点的有权无向图和 \(m\) 个关键点,求其生成树满足每个点到离它最近的关键点的距离之和最小. 输出该生成树边权和. 加强一下: 给出一个 \(N\) 个点 ...
- Nginx 反向代理的配置和注意点(成功配置)
反向代理配置成功 首先,Nginx 和 Java 后端都运行在云服务器的 docker 容器中.ps: 需要确保云服务器端口正常开放,以及两个容器都能被正常的访问. 现在想让 ng 做反向代理达到如下 ...