由于.NET和Rust存在指向堆栈的指针 ,这很难有效地实现用户模式绿色虚拟线程:
什么是绿色线程(虚拟线程)
绿色线程是用户空间线程,由运行时库或虚拟机 (VM) 而不是操作系统管理。它们很轻量,可以比内核线程更快地创建和管理。绿色线程在其他编程语言中也称为“协程”或“纤程”。
绿色线程则不同。绿色线程的内存分配在堆上。但所有这些都是有代价的:由于它们不受操作系统管理,因此它们本身无法利用多个内核。但对于 I/O 密集型操作,它们很合适。
绿色线程的调度完全在用户空间进行,操作系统内核无法识别这些线程。这意味着:
- 无法利用多核处理器:绿色线程无法在多核处理器上实现真正的并行执行,所有绿色线程在同一时刻只能在一个核心上运行,限制了性能提升的潜力。
实现用户模式绿色线程时,指向堆栈的指针管理非常复杂,这导致了许多挑战。以下是一些主要原因:
- 上下文切换:绿色线程的上下文切换需要保存和恢复线程的堆栈指针及其他寄存器状态。在用户模式下,这一过程完全依赖于用户空间的线程库,而不是内核的支持。这使得在切换线程时,必须手动管理堆栈指针,增加了实现的复杂性和出错的可能性
- 阻塞问题:如果一个绿色线程执行了阻塞操作(如I/O),整个进程可能会被阻塞,导致所有绿色线程无法继续执行。这种情况在内核级线程中可以通过调度其他线程来避免,但在用户模式下,绿色线程的调度无法被内核感知,限制了并发性
FFI 的影响:
-
外部函数接口(FFI)允许不同编程语言之间进行互操作。然而,在涉及到用户模式虚拟线程时,FFI会引入额外的复杂性。例如,当一个用户模式线程调用一个通过FFI暴露的外部函数时,可能需要保存和恢复堆栈指针,这增加了线程管理的复杂性。
对于Rust来说:
-
因为它是一种低级语言,非常注重让程序员控制小细节,它比高级语言暴露了更多的旋钮,这意味着更多的承诺。
-
它比高级语言更依赖 FFI,这当然使得很难有效地实现用户模式线程。
-
而且低级语言的内存管理成本要高得多(它们专注于低占用空间,其良好的内存性能来自于最大限度地减少对动态内存管理的依赖)。
因此,如果您有指向堆栈的指针,如果您直接将对象暴露给其他语言,并且动态内存分配成本高昂,那么很难有效地实现用户模式线程。
.NET放弃绿色线程
async2 - .NET 运行时异步实验结束(放弃绿色线程)
放弃绿色线程关键的原因挑战:
-
绿色线程与现有异步模型之间的复杂交互
-
与本机代码的互操作比使用常规线程更复杂且更慢
-
与影子堆栈等安全缓解措施的兼容性问题
-
考虑到改进所需的努力,不确定是否有可能在重要场景中使绿色线程比异步线程更快。
绿色线程不是 .NET 运行时的正确选择,并催生了async2实验。