异步机制的简单实现

2023-09-21 20:12:48

计算机有两种阻塞,一是 cpu 阻塞,二是 io 阻塞。cpu 阻塞就是 cpu 密集计算,io 阻塞比如等待网络响应,等待磁盘响应,纯粹是浪费时间。线程机制和异步机制都可避免 io 阻塞,但 cpu 阻塞的负面效果就只有线程可以避免了。
在现代计算机语言里,大量线程切换虽然会有性能问题,但是线程用起来简单,而且线程能在固定时间切换,可保证实时性,不是 io 高并发就直接用线程吧。async 和多线程并不是二选一,在同一应用中,可以根据情况两者一起使用。

https://course.rs/advance/async/getting-started.html

有大量 IO 任务需要并发运行时,选 async 模型
有部分 IO 任务需要并发运行时,选多线程,如果想要降低线程创建和销毁的开销,可以使用线程池
有大量 CPU 密集任务需要并行运行时,例如并行计算,选多线程模型,且让线程数等于或者稍大于 CPU 核心数
无所谓时,统一选多线程
async 和多线程的性能对比
操作	async	线程
创建	0.3 微秒	17 微秒
线程切换	0.2 微秒	1.7 微秒
可以看出,async 在线程切换的开销显著低于多线程,对于 IO 密集的场景,这种性能开销累计下来会非常可怕!

通过线程机制的不断切换线程可让所有线程得到执行,即使 cpu 在进行密集计算导致 cpu 占用 100% 也能保证整个系统的流畅度。
异步是在线程之内的,没有异步机制的话一个线程里就不能同时跑多个任务,多个任务只能挨个进行,就像执行普通函数那样。异步机制不会主动切换任务,只能让任务自己退出来才能进行下一个任务,如果线程里任何一个任务占据 cpu 时间过长就会显的很卡。

异步机制本质上就是非阻塞状态机(switch case),单片机常用,这是一个系统化的工程,需要让其所有功能都是非阻塞的,也就是让所有的阻塞的功能都要通过不断轮询来获得结果,然后再将这些需要查询的功能都单独放在一个 case 里。
比如一个 delay_ms() 函数,如果在线程的保存数据并切换线程的机制下实现就是查询时间到没到,没到就切换线程,到了就继续该线程。而非阻塞状态机里的实现也是查询时间,但要根据其返回值来判断是否到没到时间,到了就手动加一步,下次轮询该任务就可以执行下一步了,没到就退出,就可以轮询下一个任务了。


异步机制的简单实现:

uint32_t current_time = 0;

struct delay_t
{
	bool working;
	uint64_t end_time;
}; 

// 时间到了返回 true
bool delay_ms(delay_t* self, uint64_t ms)
{
	// 初始化一次
	if (self->working == false)
	{
		self->working = true;
		self->end_time = current_time + ms;
		return false;
	}
	
	// 如果时间没到
	if (current_time < self->end_time)
	{
		self->working = false;
		return false;
	}

	return true; // 时间到了
}

void task_1()
{
	static uint8_t step = 0;
	static delay_t delay = {0};
	
	switch (step)
	{
		case 0:
			printf("task_1");
			step++;
		break;
		
		case 1:
			if(delay_ms(&delay, 500))
				step = 0;
		break;
	}
}

void task_2()
{
	static uint8_t step = 0;
	static delay_t delay = {0};
	
	switch (step)
	{
		case 0:
			printf("task_2");
			step++;
		break;
		
		case 1:
			if(delay_ms(&delay, 1000))
				step = 0;
		break;
	}
}

// 类似 delay_ms() 这样阻塞的都要改成轮询式
int main()
{
	while (true)
	{
		current_time++; // 模拟当前时间
	
		task_1();
		task_2();
	}
}
更多推荐

golang http

函数说明http.ServeMux是Go语言标准库中的一个多路复用器(multiplexer)。它用于路由和处理HTTP请求,将请求分发到相应的处理器函数。http.HandleFunc是Go语言标准库中的一个函数,用于注册处理器函数来处理HTTP请求。它是对http.ServeMux的简化封装,方便快速实现路由功能。

微信小程序通过 wxministore 实现类似于vuex的全局装填数据管理

首先我们打开终端引入依赖npminstallwxministore--save然后如果你是新版开发者工具就npmi构建一下如果你是老版本的微信开发者工具就打开右上角详情选择本地管理勾选使用npm模块然后在根目录下创建一个store.js当然建在哪是你自己决定的反正后面能引入到就好然后store.js编写代码如下impo

Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单

项目说明随着公司的快速发展,企业人员和经营规模不断壮大,公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境,最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范,以及审计监督要求;通过电子化平台提高招投标工作的公开性和透明性;通过电子化招投标,使得招标采购的质量更高、速度

MyBatis 缓存模块

文章目录前言缓存的实现Cache接口PerpetualCache缓存的应用缓存对应的初始化一级缓存二级缓存第三方缓存前言MyBatis作为一个强大的持久层框架,缓存是其必不可少的功能之一,Mybatis中的缓存分为一级缓存和二级缓存。但本质上是一样的,都是使用Cache接口实现的。缓存的实现Cache接口Cache接口

WebGL 初始化着色器

目录前言初始化着色器的7个步骤创建着色器对象(gl.createShader())gl.createShader()规范gl.deleteShader()规范指定着色器对象的代码(gl.shaderSource())gl.shaderSource()规范编译着色器(gl.compileShader())gl.compi

动力节点老杜JavaWeb笔记(全)

Servlet关于系统架构系统架构包括什么形式?C/S架构B/S架构C/S架构?Client/Server(客户端/服务器)C/S架构的软件或者说系统有哪些呢?QQ(先去腾讯官网下载一个QQ软件,几十MB,然后把这个客户端软件安装上去,然后输入QQ号以及密码,登录之后,就可以和你的朋友聊天了,就可以使用这个软件了。)C

为何学linux及用处

为何学linux及用处目前企业使用的操作系统无非就是国产类的,windows和linux类。我们要提升自己的技能,需要学习这两款。我记得在大学时期,学习过windows以及linux,但当时觉得又不常用,就学的模棱两可。毕业之后,你会发现,其实这两种操作系统是很主流的。为什么学?下面就是一些工作中遇到的例子分享一下。我

golang 结合 cobra 使用 chatgpt qdrant 实现 ai知识库 cli

golang结合cobra使用chatgptqdrant实现ai知识库cli流程将数据集通过openaiembedding得到向量+组装payload,存入qdrant用户进行问题搜索,通过openaiembedding得到向量,从qdrant中搜索相似度大于0.8的数据从qdrant中取出数据得到参考答案将问题标题+

js兼容性的汇总

js兼容问题大多是在ie浏览器低版本以及ie浏览器和不同普通浏览器的差异事件对象document.onclick=function(ev){//谷歌火狐的写法,IE9以上支持,往下不支持;vare=ev;console.log(e);}document.onclick=function(){//谷歌和IE支持,火狐不支

css3动画基础详解(@keyframes和animation)

动画是使元素从一种样式逐渐变化为另外一种效果,CSS3动画的生成,主要依赖@keyframes定义动画,animation执行动画。@keyframes通过@keyframes规则创建动画。@keyframeskeyframes-name{keyframes-selector{css-styles;}}keyframe

【数据结构】哈希表(详)

文章目录前言正文一、基本概念二、基本原理1.哈希函数1.1直接定址法(常用)1.2除留余数法(常用)1.3平方取中法(了解)1.4折叠法(了解)1.5随机数法(了解)1.6数学分析法(了解)2.哈希冲突2.1平均查找长度2.2负载因子2.3闭散列(开放定制法)2.1.1线性探测2.1.2二次探测2.1.3二重哈希2.4

热文推荐