您可以通过复制堆栈帧而不是创建状态机来实现异步等待吗? 进一步阅读 TLDR

如何解决您可以通过复制堆栈帧而不是创建状态机来实现异步等待吗? 进一步阅读 TLDR

我试图理解所有低级的东西编译器/解释器/内核为您服务(因为我还是另一个人,他们认为他们可以设计一种比大多数其他语言都更好的语言)

引发我好奇的许多事情之一是Async-Await。 我已经检查了几种语言的幕后实现,包括C#(编译器从糖代码生成状态机)和Rust(其中状态机必须根据Future特性手动实现),它们都使用状态机实现Async-Await。 通过谷歌搜索(“异步副本堆栈框架”和其他形式)或在“类似问题”部分中,我没有发现任何有用的东西。

在我看来,这种方法相当复杂且负担重;

您是否可以仅通过复制到/从堆的异步调用的堆栈帧来实现Async-Await?

我知道某些语言在架构上是不可能的(我感谢CLR无法做到这一点,所以C#也不能做到)

我是否错过了一些在逻辑上不可能做到的事情?我希望这样做会减少复杂的代码并提高性能,这是我误会了吗?我想当异步调用(例如递归异步函数)之后您拥有较深的堆栈层次结构时,您必须进行内存复制的数据量很大,但是可能有解决方法。

如果可能的话,为什么不在任何地方做呢?

解决方法

是的,将代码转换为状态机的另一种方法是复制堆栈。这是go语言现在执行此操作的方式,也是发布Project Loom时Java将执行此操作的方式。

对于现实世界的语言而言,这并非易事。

例如,它不适用于C和C ++,因为这些语言使您可以指向堆栈中的内容。这些指针可由其他线程使用,因此您无法将堆栈移开,即使可以,您也必须将其复制回完全相同的位置。

出于相同的原因,当您的程序调出OS或本机代码并在同一线程中被调回时,它不起作用,因为其中有一部分是您无法控制的。在Java中,只要堆栈上有本机代码,Project Loom的“虚拟线程”就不会释放该线程。

即使在可以移动堆栈的情况下,它也需要运行时环境中的专用支持。堆栈不能只是复制到字节数组中。它必须以一种表示形式进行复制,该表示形式允许垃圾收集器识别其中的所有指针。例如,如果C#采用此技术,则将需要对公共语言运行时进行重大扩展,而状态机的实现可以完全在C#编译器中完成。

,

首先,我想说的是,这个答案仅是作为您实际探索方向的起点。这包括各种指针,并以其他作者的作品为基础

我已经检查了几种语言的幕后实现,包括C#(编译器从Sugar代码生成状态机)和Rust(其中状态机必须根据Future特性来手动实现),他们都使用状态机实现Async-Await

您正确理解C#Rust的Async / Await实现使用状态机。现在让我们了解为什么选择这些实现。

为了简单地说明堆栈框架的一般结构,我们放入堆栈框架中的任何内容都是临时分配,不会使该方法超过导致添加该堆栈框架的方法(包括但不限于)局部变量)。它还包含继续信息,即。在最近调用的方法的上下文中,下一个需要执行的代码的地址(换句话说,控件必须返回)。如果这是同步执行的情况,则将一个接一个地执行这些方法。换句话说,调用方方法被挂起,直到被调用方法完成执行。从堆栈的角度来看,这很直观。如果执行完一个被调用的方法,控件将返回给调用者,并且可以弹出堆栈框架。从运行此代码的硬件的角度来看,它也是便宜且高效的(针对栈编程,对硬件进行了优化)。

在异步代码的情况下,方法的继续可能必须触发其他几个方法,这些方法可能在调用者的继续方法中被调用。看一下this answer,其中Eric Lippert概述了堆栈如何用于异步流。异步流的问题在于,方法调用不能完全形成堆栈,而像纯堆栈一样尝试处理它们可能变得极其复杂。正如埃里克(Eric)在回答中所说,这就是C#使用代表堆的工作分配的任务和委托的图形的原因。

但是,如果您考虑使用Go之类的语言,则会以完全不同的方式处理异步问题。我们有一个叫做Goroutines的东西,这里await中不需要Go语句。这些Goroutines中的每个都在自己的轻量级线程上启动(每个线程都有自己的堆栈,默认大小为8KB),并且它们之间的同步是通过channels进行通信的。这些轻量级线程能够异步等待要在通道上执行的任何读取操作并使其自身挂起。 Go中较早的实现是使用SplitStacks技术完成的。此实现有自己的问题,列在here中,并由 Contigious Stacks 取代。本文还讨论了较新的实现。

这里要注意的重要一件事是,处理任务之间的连续性所涉及的复杂性不仅对选择实现Async / Await的方法有所帮助,而且还有其他因素,例如垃圾收集发挥作用。 GC过程应尽可能地高效。如果我们移动堆栈,GC将变得效率低下,因为访问对象将需要线程同步。

您能否仅通过复制到/从堆的异步调用的堆栈帧来实现Async-Await?

简而言之,可以。正如this answer在此处所述,Chicken Scheme使用的内容与您正在探索的内容类似。它首先在堆栈上分配所有内容,然后在堆栈大小对于GC活动而言变得太大时将堆栈值移动到堆中(Chicken Scheme使用Generational GC)。但是,这种实现存在一定的注意事项。看看这个FAQ的Chicken Scheme。您可能想看看这方面的很多学术研究(与本段开头提到的答案相关,我将在以后的阅读中进行总结)。

进一步阅读

Continuation Passing Style

call-with-current-continuation

The classic SICP book

This answer(与该领域学术研究的链接很少)

TLDR

采用哪种方法的决定取决于影响语言整体可用性和性能的因素。状态机不是实现C#Rust中完成异步/等待功能的唯一方法。很少有类似Go的语言会在通道上实现Contigious Stack的方法,以进行异步操作。 Chicken Scheme分配堆栈上的所有内容,并将最近的堆栈值移到堆中,以防GC算法的性能变差。移动堆栈有其自身的含义,会对垃圾回收产生负面影响。在此空间中进行的研究将帮助您了解每种方法背后的进步和原理。同时,您还应该考虑如何计划设计/实现语言的其他部分,因为就性能和整体可用性而言,该语言应尽可能接近可用。

PS:鉴于此答案的长度,我们将很乐意纠正可能造成的任何不一致。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-