open-coroutine-core 0.7.0

The open-coroutine is a simple, efficient and generic coroutine library.
Documentation
---
title: 协程总览
date: 2024-12-29 16:00:00
author: loongs-zhang
---

# 协程总览

[English](../en/coroutine.md) | 中文

## 使用方法

```rust
use open_coroutine_core::common::constants::CoroutineState;
use open_coroutine_core::coroutine::Coroutine;

fn main() -> std::io::Result<()> {
    let mut co = Coroutine::new(
        // 可选的协程名称
        None,
        |suspender, input| {
            assert_eq!(1, input);
            assert_eq!(3, suspender.suspend_with(2));
            4
        },
        // 可选的栈大小
        None,
        // 可选的协程优先级
        None,
    )?;
    // 宏`co!`等同于上面的代码
    // let mut co = open_coroutine_core::co!(|suspender, input| {
    //     assert_eq!(1, input);
    //     assert_eq!(3, suspender.suspend_with(2));
    //     4
    // })?;
    assert_eq!(CoroutineState::Suspend(2, 0), co.resume_with(1)?);
    assert_eq!(CoroutineState::Complete(4), co.resume_with(3)?);
    Ok(())
}
```

## 什么是协程?

[协程](https://en.wikipedia.org/wiki/Coroutine)是一种可以暂停和恢复的函数,能够向调用者返回值。协程可以在其调用栈的任何位置挂起自己。除了从协程中接收返回值外,你还可以在每次恢复协程时向其传递数据。

以上内容摘自[corosensei](https://github.com/Amanieu/corosensei)。

## 协程 VS 线程

|         | 协程            | 线程     |
|---------|----------------|----------|
| 切换效率 | ✅ 更高         | ❌ 高    |
| 内存用量 | ✅ Bytes/KB/MB | ❌ KB/MB |
| 由OS调度 | ❌             | ✅       |
| 可伸缩栈 | ✅             | ❌       |

## 有栈协程 VS 无栈协程

|         | 有栈协程  | 无栈协程  |
|---------|----------|----------|
| 切换效率 | ❌ 高    | ✅ 更高   |
| 内存用量 | ❌ KB/MB | ✅ Bytes |
| 使用限制 | ✅ 较少   | ❌ 较多  |

一般来说,如果对资源利用率和切换性能的要求不是非常严格,使用有栈协程会更加方便,代码也更容易维护。因此,`open-coroutine`选择了有栈协程。

## open-coroutine中的状态

```text
           Ready
        ↗    ↓
Suspend ← Running ⇄ Syscall
           ↙   ↘
      Complete Error
```

在open-coroutine中,创建的协程处于`Ready`状态,一旦调用`Coroutine::resume_with`方法,状态将从`Ready`变为`Running`。之后,协程可能会通过`Suspender::suspend_with`挂起,状态将从`Running`变为`Suspend`,`Suspend`状态还会记录可以被唤醒的时间戳,单位为纳秒。

当协程进入系统调用时,协程状态将从`Running`变为`Syscall`,系统调用完成后,状态将从`Syscall`变回`Running`(注意:如果你使用[open-coroutine-core](https://crates.io/crates/open-coroutine-core),你需要在适当的时候手动调用`Coroutine::syscall`和`Coroutine::running`来切换协程状态,这会增加大量工作量且容易出错,因此请使用[open-coroutine](https://crates.io/crates/open-coroutine)并启用`hook`)。此外,系统调用`Syscall`状态会记录系统调用的名称和一个用于open-coroutine内部的状态。

当协程成功完成时,状态将从`Running`变为`Complete`。如果在协程执行过程中发生panic且用户未处理该panic,状态将从`Running`变为`Error`同时记录panic信息。

## `Listener` 设计

To enhance extension, we provide the `Listener` API, which notifies `Listener` whenever Coroutine state changes.

为了增强扩展性,我们提供了`Listener`API,每当协程状态发生变化时,都会通知`Listener`。

## `CoroutineLocal` 设计

`ThreadLocal`的设计意图是解决多线程环境中的线程安全问题。在多线程程序中,多个线程可能同时访问和修改同一个共享变量,这可能导致线程安全问题,如数据不一致和竞态条件。为了解决这些问题,传统方法是通过锁来同步对共享资源的访问,但这可能导致性能下降,尤其是在高并发场景中。`ThreadLocal`提供了一种新的解决方案,它为每个线程提供变量的独立副本,从而避免了多个线程之间的共享变量冲突,确保了线程安全,并提高了程序的并发性能。

在协程环境中,尽管调度线程是单线程的,但由于工作窃取机制的存在,协程可能会在多个线程之间流动,这使得`ThreadLocal`失效。为了解决这个问题,我们不得不引入`CoroutineLocal`。它与`ThreadLocal`的副本提供方式类似,但`CoroutineLocal`将副本升级到了协程级别,这意味着每个协程都有自己的局部变量。这些局部变量将在协程被销毁时一起被丢弃。

## [可伸缩栈总览](scalable-stack.md)