22 Matching Annotations
  1. Nov 2023
    1. See bear, this is what I'm talking about: there's just so much I have to take for granted. "Don't block the executor", "futures do nothing unless polled", "you need to pin a future before you can poll it", etc.

      看啊,熊,这就是我所说的:有太多东西我必须视为理所当然。比如,“不要阻塞执行器”,“未来任务只有在被轮询时才会执行任何操作”,“你需要在轮询之前固定一个未来任务”等等。

      解释: - "Don't block the executor"(不要阻塞执行器):指的是在使用异步编程框架时,应避免在执行器中出现长时间的阻塞操作,以充分利用并发性能。 - "Futures do nothing unless polled"(未来任务除非被轮询否则无效):表示异步任务只有在被主动轮询时才会进行进一步处理和执行。 - "You need to pin a future before you can poll it"(你需要先固定一个未来任务再进行轮询):意味着在对某个异步任务进行轮询之前,需要将其绑定到特定的内存位置上以确保安全性和正确性。

    2. Eh, not exactly — you can build an executor in a blue (sync) function to block until a red (async) function is done. And from a red (async) function, you can use something like tokio::task::spawn_blocking. I guess the difficulty is when you go from red to blue, back to red — because you can't make an executor inside an executor.

      嗯,并不完全准确——你可以在一个蓝色(同步)函数中构建一个执行器,以阻塞直到红色(异步)函数完成。而从红色(异步)函数中,你可以使用类似tokio::task::spawn_blocking的方法。我猜问题出现在从红色转换为蓝色,再回到红色时——因为你不能在执行器内部创建另一个执行器。

      解释: - "executor":指的是任务调度和管理的机制,在异步编程中用于处理并发任务。 - "blue (sync) function":指的是同步函数,即按顺序逐行执行代码。 - "red (async) function":指的是异步函数,即可暂停和恢复执行的代码块。 - "block until a red (async) function is done":意思是等待直到某个异步函数完成。 - "tokio::task::spawn_blocking":Tokio库提供的功能之一,用于将同步代码块包装成异步任务进行处理。

    3. And to become proficient with async Rust, I've accepted a lot of things. There are blue functions and red functions, and red (async) functions are contagious.

      为了熟练掌握异步Rust,我接受了很多事情。有蓝色函数和红色函数,而红色(异步)函数是具有传染性的。

      解释: 这段话描述了作者在学习异步Rust过程中所面对的一些概念。其中提到了"blue functions"和"red functions",这里可以理解为不同类型的函数。而"red (async) functions are contagious."则表示红色(即异步)函数具有传染性,意味着使用异步函数可能会影响到其他相关代码或者需要进行相应调整以适应异步操作。

    4. I'd like to think that my understanding of "async Rust" has increased over the past year or so. I'm 100% onboard with the basic principle: I would like to handle thousands of concurrent tasks using a handful of threads. That sounds great!

      我认为在过去的一年左右,我对“异步Rust”有了更深入的理解。我完全支持基本原则:我希望能够使用少量线程处理成千上万个并发任务。这听起来很棒!

      解释: - "async Rust"指的是使用Rust编程语言中的异步编程模式。 - "concurrent tasks"表示同时进行的任务。 - "threads"指代计算机程序中执行代码的独立执行单元。

    1. A big issue for any green threading system - Rust’s or Go’s or any other language’s - is what to do about the program stack for these threads. Remember that one of the goals of a user-space concurrency mechanism is to reduce the memory overhead of the large, pre-allocated stack used by OS threads. Therefore, green thread libraries tend to try to adopt a mechanism to spawn threads with smaller stacks, and grow them only as needed.

      任何绿色线程系统(如Rust、Go或其他语言)都面临一个重大问题,即如何处理这些线程的程序堆栈。请记住,用户空间并发机制的目标之一是减少操作系统线程所使用的大型预分配堆栈的内存开销。因此,绿色线程库倾向于采用一种机制来生成具有较小堆栈的线程,并仅在需要时扩展它们。

      解释: - Green threading system:指基于用户空间实现的轻量级并发机制。 - Rust's or Go's or any other language's:指Rust、Go或其他编程语言。 - Program stack:程序堆栈,用于保存函数调用和局部变量等信息。 - User-space concurrency mechanism:用户空间并发机制,在应用程序层面实现并发而不依赖操作系统提供的多线程支持。 - Memory overhead:内存开销,指为了支持多个OS线程而预先分配给每个线程的内存空间。 - Pre-allocated stack:预分配堆栈,在创建OS线程时就会为其分配固定大小的堆栈空间。 - Spawn threads with smaller stacks:以较小大小生成新线程,在开始时只分配较小数量的堆栈空间。 - Grow them only as needed:按需增长,当需要更多堆栈空间时再进行动态扩展。

    2. Setting aside the references to structured concurrency, channels and a work stealing executor (completely orthogonal concerns), the bewildering thing about comments like this is that originally Rust did have a stackful coroutine mechanism, in the form of green threads. It was removed in late 2014, shortly before the 1.0 release. Understanding the reason why will help us get to the bottom of why Rust shipped async/await syntax.

      撇开对结构化并发、通道和工作窃取执行器(完全无关的问题)的引用,令人困惑的是,像这样的评论中最初Rust确实有一种堆栈协程机制,即绿色线程。它在2014年底,在1.0版本发布之前被移除了。理解其中原因将帮助我们弄清楚为什么Rust采用了异步/等待语法。

      解释: - "Setting aside"意思是“撇开”、“不考虑”,表示暂时忽略或不讨论某些内容。 - "references to"指对某个主题或概念的提及或涉及。 - "structured concurrency"是一种编程模式,用于管理并发任务和资源。 - "channels"指通信机制,用于在多个协程或线程之间传递数据。 - "work stealing executor"是一种调度算法,用于平衡负载和提高并行性能。 - "bewildering thing about comments like this"表示对类似评论感到困惑的事情。 - "originally Rust did have a stackful coroutine mechanism, in the form of green threads."意思是最初Rust确实有一种堆栈协程机制,即绿色线程。 - "It was removed in late 2014, shortly before the 1.0 release."表示该机制在2014年末,在1.0版本发布之前被移除了。 - "Understanding the reason why will help us get to the bottom of why Rust shipped async/await syntax."意思是理解其中原因将帮助我们弄清楚为什么Rust采用了异步/等待语法。

    3. The alternative concurrency model people want is structured concurrency via stackful coroutines and channels on top of a work stealing executor.Until someone does the work to demo that and compare it to async/await with futures I don’t think there’s any productive discussion to be had.

      人们想要的替代并发模型是通过基于堆栈式协程和通道的结构化并发,以及在工作窃取执行器之上实现。

      在有人进行演示并将其与使用未来对象的异步/等待方式进行比较之前,我认为没有任何富有成效的讨论可以展开。

      解释: - "alternative concurrency model":替代性并发模型,指一种不同于传统线程或进程模型的处理并发任务的方法。 - "structured concurrency":结构化并发,指一种通过明确定义任务间关系和依赖关系来管理和控制多个同时运行任务的方法。 - "stackful coroutines":堆栈式协程,是一种轻量级线程(称为协程)实现方式,在执行过程中可以保存当前状态,并能够暂停、恢复和切换执行上下文。 - "channels":通道,在并发编程中用于协调不同任务之间数据传输和共享信息。 - "work stealing executor":工作窃取执行器,是一种用于调度和分配任务到可用处理资源(如线程)上的机制。它允许空闲处理资源主动从其他繁忙资源那里“偷取”未完成的子任务来提高整体效率。 - "async/await with futures":异步/等待与未来对象,在某些编程语言中,通过使用异步关键字和等待操作来实现并发任务的执行和结果获取。未来对象是一种表示尚未完成的计算结果的占位符。

      总之,作者认为在没有对比展示基于堆栈式协程和通道的结构化并发模型与使用异步/等待与未来对象方式进行比较之前,讨论这个话题是没有意义的。

    4. A third Hacker News comment represents well the kind of remark that I often see in this debate:

      Hacker News 的第三条评论很好地诠释了我在这场辩论中经常看到的那种言论:

    5. Rust’s async/await syntax is an example of a stackless coroutine mechanism: an async function is compiled to a function which returns a Future, and that future is what is used to store the state of the coroutine when it yields control. The basic question at hand in this debate is whether Rust was correct to adopt this approach, or if it should have adopted a more Go-like “stackful” or “green thread” approach, ideally without explicit syntax that “colors” functions.

      Rust的async/await语法是无栈协程机制的一个例子:异步函数被编译为返回Future的函数,而这个Future用于存储协程在让出控制权时的状态。在这场辩论中,基本问题是Rust是否正确采用了这种方法,或者它是否应该采用更类似Go的“有栈”或“绿色线程”的方法,理想情况下不需要显式语法来“着色”函数。

      解释: - Rust's async/await syntax: Rust编程语言中使用的异步/等待(async/await)语法。 - stackless coroutine mechanism: 无栈协程机制,指一种不依赖调用堆栈实现协程切换和保存状态的机制。 - async function: 异步函数,在执行过程中可以暂停并恢复执行。 - Future: 表示一个可能尚未完成但最终会产生结果的计算任务。 - yield control/yields control: 让出控制权,在异步函数执行过程中暂停当前任务,并将控制权交给其他任务。 - Go-like "stackful" or "green thread" approach: 类似Go语言中所使用的具有调用堆栈(stackful)或绿色线程(green thread)特性的方法。

    6. One issue that is often brought up with async/await (in Rust and other languages) is the “function coloring problem” - a complaint that in order to get the result of an async function, you need to use a different operation (such as awaiting it) rather than call it normally. Both green threads and stackful coroutine mechanisms can avoid this outcome, because it’s that special syntax that is used to indicate that something special is happening to manage the stackless state of the coroutine (what specifically depends on the language).

      使用async/await(在Rust和其他语言中)经常出现的一个问题是“函数着色问题”——为了获得async函数的结果,您需要使用不同的操作(例如等待)而不是正常调用它。绿色线程和堆栈协同程序机制都可以避免这种结果,因为这种特殊的语法用于指示正在发生一些特殊的事情来管理协同程序的无堆栈状态(具体取决于语言)。

    7. The second axis of choice is between a stackful and a stackless coroutine. A stackful coroutine has a program stack in the same way that an OS thread has a program stack: as functions are called as part of the coroutine, their frames are pushed on the stack; when the coroutine yields, the state of the stack is saved so that it can be resumed from the same position. A stackless coroutine on the other hand stores the state it needs to resume in a different way, such as in a continuation or in a state machine. When it yields, the stack it was using is used by the operation that took over from it, and when it resumes it takes back control of the stack and that continuation or state machine is used to resume the coroutine where it left off.

      选择的第二个维度是在堆栈式和无堆栈式协程之间进行选择。堆栈式协程与操作系统线程类似,具有程序堆栈:当作为协程的一部分调用函数时,它们的帧被推入堆栈;当协程暂停时,保存了堆栈状态以便可以从同一位置恢复执行。另一方面,无堆栈式协程以不同的方式存储需要恢复执行所需的状态,例如在一个continuation(延续)或者状态机中。当它暂停时,接管控制权的操作使用了它之前使用过的堆栈,并且在恢复执行时重新获得对该堆栈的控制权,并使用continuation或者状态机来从离开处继续执行协程。

      解释: - 堆栈式协程(stackful coroutine):类似于操作系统线程,在调用函数时将其帧推入程序堆栈,并通过保存当前位置来实现暂停和恢复。 - 无堆栈式协程(stackless coroutine):采用其他方式存储需要恢复执行所需的状态,在暂停期间由其他操作接管控制权并利用原先使用过的程序堆栈,在恢复后重新获得对该程序堆 格 的控制权,并利用continuation或者状态机从离开处继续执行协程。

    8. Goroutines, on the other hand, are a Go language feature which enables concurrent, preemptively scheduled tasks. They have an API that is the same as a thread, but it is implemented as part of the language instead of as an operating system primitive, and in other languages they are often called virtual threads or else green threads. So by my definition, goroutines are not coroutines, but other people use the broader definition and say goroutines are a kind of coroutine. I’ll refer to this approach as green threads, because that’s been the terminology used in Rust.

      另一方面,Goroutines是Go语言的一个特性,它可以实现并发、抢占式调度的任务。它们具有与线程相同的API,但是它们是作为语言的一部分而不是作为操作系统原语来实现的,在其他编程语言中通常被称为虚拟线程或绿色线程。所以根据我的定义,Goroutines不是协程,但其他人使用更广泛的定义,并说Goroutines是一种协程。我将这种方法称为绿色线程,因为在Rust中已经使用了这个术语。

      解释: - Goroutines:指Go语言中用于实现并发任务调度的特性。 - Concurrent:并发执行多个任务。 - Preemptively scheduled tasks:抢占式调度任务,在多个任务之间进行切换执行。 - API:应用程序接口(Application Programming Interface),提供给开发者使用和交互的函数集合。 - Thread:线程,在计算机程序中负责执行代码片段。 - Operating system primitive:操作系统原语,指操作系统提供给开发者直接访问底层功能和资源的接口或工具。 - Virtual threads:虚拟线程,在某些编程语言中模拟出来具备类似于真正线程行为和功能的概念或机制。 - Coroutine:协程,在计算机科学领域指一种协作式的多任务处理方式。 - Green threads:绿色线程,在Rust编程语言中指类似于Goroutines或虚拟线程的概念。

    9. A term that gets thrown around a lot in these discussions is coroutine, and it is used in somewhat contradictory ways. A coroutine is a function which can be paused and then later resumed. The big ambiguity is that some people use the term “coroutine” to mean a function which has explicit syntax for pausing and resuming it (this would correspond to a cooperatively scheduled task) and some people use it to mean any function that can pause, even if the pause is performed implicitly by a language runtime (this would also include a preemptively scheduled task). I prefer the first definition, because it introduces some manner of meaningful distinction.

      在这些讨论中,经常会提到一个术语叫做"coroutine"(协程),而且它的使用方式有些矛盾。协程是一种可以暂停然后稍后继续执行的函数。其中存在一个大的模糊点,就是有些人使用"coroutine"这个术语来表示具有显式语法以便暂停和恢复函数执行的功能(这对应于合作调度任务),而其他人则将其用来表示任何能够暂停的函数,即使该暂停是由编程语言运行时隐式地执行的(这也包括抢占式调度任务)。我更倾向于第一种定义,因为它引入了某种有意义的区别。

      解释: - Coroutine:协程,在计算机科学中指一种特殊类型的函数。 - Function:函数,在程序设计中指接收输入并产生输出结果或者完成特定任务的代码块。 - Paused and resumed:暂停和恢复,在此上下文中指在执行过程中临时停止,并在稍后重新开始。 - Ambiguity:模糊性、不明确性,在此上下文中指对术语含义存在不同理解或解释。 - Explicit syntax:显式语法,在此上下文中指通过特定关键字或操作符明确地控制程序流程。 - Cooperatively scheduled task:合作调度任务,在此上下文中指由程序员显式地控制任务的执行顺序。 - Implicitly:隐式地,在此上下文中指通过编程语言运行时自动处理某些操作或行为。 - Preemptively scheduled task:抢占式调度任务,在此上下文中指由操作系统或编程语言运行时决定任务的执行顺序。

    10. The first axis of choice in this design space is between cooperative and preemptive scheduling. Must tasks “cooperatively” yield control back to the scheduling subsystem, or can they be “preemptively” stopped at some point while they’re running, without the task being aware of it?

      在这个设计空间中,第一个选择轴是合作式调度和抢占式调度之间的选择。任务是否需要“合作地”将控制权交还给调度子系统,或者它们可以在运行过程中被“抢占地”停止而不让任务察觉到?

      解释: - 合作式调度:指任务自愿将控制权交还给调度子系统,在适当的时候主动放弃执行。 - 抢占式调度:指任务可能会在任何时间点被强制性地停止执行,无需事先通知或同意。

      该句话描述了一种设计空间中的选择问题,即关于任务如何与调度子系统进行交互以及对资源分配进行管理的方式。其中涉及到两种不同的策略:合作式和抢占式。合作式要求任务自愿放弃控制权并返回给调度子系统;而抢占式则允许在某些情况下直接停止正在运行的任务,并且无需事先通知或征得其同意。

    11. These limitations are fine up to a certain point, but for massively concurrent programs they do not work. The solution is to use a non-blocking IO interface and schedule many concurrent operations on a single OS thread. This can be done by the programmer “by hand,” but modern languages frequently provide facilities to make this easier. Abstractly, languages have some way of dividing work into tasks and scheduling those tasks onto threads. Rust’s system for this is async/await.

      这些限制在某种程度上是可以接受的,但对于大规模并发程序来说,它们不起作用。解决方案是使用非阻塞IO接口,并在单个操作系统线程上安排许多并发操作。程序员可以通过手动方式实现这一点,但现代编程语言通常提供了简化此过程的工具。从抽象角度看,编程语言有一种将工作划分为任务并将这些任务调度到线程上的方法。Rust中实现这一功能的机制是async/await。

      解释: - "limitations"指的是前文提到的某些限制条件。 - "massively concurrent programs"指大规模并发程序。 - "non-blocking IO interface"指非阻塞IO接口,即能够同时处理多个IO操作而无需等待每个操作完成后再进行下一个操作。 - "schedule many concurrent operations on a single OS thread"意味着在一个单独的操作系统线程上安排执行许多并发操作。 - "by hand"表示手动地进行相关设置和调整。 - "facilities"指编程语言提供的便利设施或工具。 - "dividing work into tasks and scheduling those tasks onto threads"意味着将工作划分为任务,并将这些任务调度到不同线程上执行。 - async/await 是 Rust 编程语言中用于实现异步编程模型(asynchronous programming model)的机制。

    12. Context-switching between the kernel and userspace is expensive in terms of CPU cycles.OS threads have a large pre-allocated stack, which increases per-thread memory overhead.

      上下文切换是指在内核和用户空间之间进行切换,这个过程对CPU周期来说是昂贵的。操作系统线程具有一个大型预分配的堆栈,这增加了每个线程的内存开销。

      解释: 1. 上下文切换:当操作系统从内核态(kernel mode)切换到用户态(user mode)或者反之时,需要保存当前执行状态并加载新的执行状态。这种切换会消耗大量的CPU资源和时间。 2. 内存开销:每个操作系统线程都需要拥有自己独立的堆栈空间用于函数调用、局部变量等。为了避免频繁地动态分配和释放堆栈空间,操作系统会提前为每个线程分配一块较大的固定大小堆栈空间。然而,这也意味着每个线程都要占用额外的内存资源。

    13. The first concept we need to get straight is the very purpose of the feature: “user-space concurrency.” The major operating systems present a set of fairly similar interfaces to achieve concurrency: you can spawn threads, and perform IO on those threads using syscalls, which block that thread until they complete. The problem with these interfaces is that they involve certain overheads that can become a limiting factor when you want to achieve certain performance targets. These are two-fold:

      我们首先需要明确的概念是该功能的目的:“用户空间并发”。主要操作系统提供了一组相当类似的接口来实现并发:您可以生成线程,并在这些线程上使用系统调用进行IO操作,这会阻塞该线程直到完成。这些接口存在问题,即它们涉及某些开销,当您想要达到特定性能目标时,这些开销可能成为限制因素。其中有两个方面:

    14. The basic issue at stake in this debate is Rust’s decision to use a “stackless coroutine” approach to implementing user-space concurrency. A lot of terms are thrown around in this discussion and its reasonable not to be familiar with all of them.

      这场争论的关键问题是Rust决定使用“无堆栈协程”方法来实现用户空间并发性。在这个讨论中有很多术语,不熟悉它们是合理的。

    15. Between 2017 and 2019, I drove the design of async/await syntax, in collaboration with others and building on the work of those who came before me. Forgive me if I am put a bit off when someone says that they don’t know how anyone could look at that “mess” and “think that it was a good design,” and please indulge me in this imperfectly organized and overly long explanation of how async Rust came to exist, what its purpose was, and why, in my opinion, for Rust there was no viable alternative. I hope that along the way I might shed more light on the design of Rust in a broader and deeper sense, at least slightly, and not merely regurgitate the justifications of the past.

      在2017到2019年间,我和其他人在其他人的工作基础上推进了 async/await 的设计。我无法认同一些人类似“怎么会有人看到这一团遭还会觉得它是一个好设计”的言论。对于 Async Rust 的出现、它的目标是什么、以及为什么是这样子的,我会进行冗长的讲述(原谅我)。我认为对 Rust 来说,没有其他可行的代替方案。我希望在这个过程中,能够以更广泛和深入的意义上对Rust的设计进行一些阐述,至少是稍微地,并不仅仅重复过去的理由。

    16. Of course, neither of these comments are completely representative: even four years ago, some people had pointed concerns. And in the same thread as this comment about jaws clenching and vision blurring, there were many people defending async Rust with equal fervor. But I don’t think I would be out of pocket to say that the nay-sayers have grown more numerous and their tone more strident as time has gone on. To some extent this is just the natural progression of the hype cycle, but I also think as we have become more distant from the original design process, some of the context has been lost.

      当然,这两种观点都不是绝对正确的:即使在四年前,一些人也提出了担忧。而且,在与这条评论相同的讨论中,同样还有很多人为极力异步 Rust 进行辩护。随着时间的推移,反对者确实变得更多了,他们的反对言辞变得更加强硬,我认为也是合理的。在某种程度上,这只是炒作周期自然发展(Hype Cycle)的必经之路,但我也认为随着我们与最初设计过程越来越远,一些背景信息已经失去了。

    17. I genuinely can’t understand how anybody could look at the mess that’s Rust’s async and think that it was a good design for a language that already had the reputation of being very complicated to write.I tried to get it, I really did, but my god what a massive mess that is. And it contaminates everything it touches, too. I really love Rust and I do most of my coding in it these days, but every time I encounter async-heavy Rust code my jaw clenches and my vision blurs.

      我无法理解为什么会有人觉得:对于一个已经以复杂著称的Rust来说,这种 Aysnc 实现还能称得上是一个好设计。 我曾尝试去理解它,但还是无法接受,它会污染所有跟它有关的代码。我很喜欢 Rust,同时它也是我最常用的编程语言,但每次我在编写异步密集的代码时,我牙关就会开始打颤、视线也变得模糊。