C++中协程——State Threads

一、协程简介

1、简介

       协程(coroutine)不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。一个程序可以包含多个协程,可以对比与一个进程包含多个线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制。而协程也相对独立,有自己的上下文,但是其切换由自己控制,当前协程切换到其他协程由当前协程来控制。

       协程调度单位减小到函数,上下文切换不需要内核参与,不存在系统调用。上下文切换开销降到最低,系统调用降到最低,没有锁竞争,没有信号处理。保留了程序对请求的线性处理逻辑,提高了程序的开发效率,可扩展性和可维护性。协程是一种程序组件。通常我们把协程理解为是一种程序自己实现调度、用于提高运行效率、降低开发复杂度的东西。而State-Thread(以下简称st),就是一个由C语言编写的小巧、简洁却高效的开源协程库。这个库基于单线程运作、不强制占用用户线程,给予了开发者最大程度的轻量级和较低的侵入性。

       很多语言都拥有协程,例如python或者golang。而对于c/c++而言,通常实现协程的常见方式,通常是依赖于glibc提供的setjump&longjump或者基于汇编语言,当然还有基于语义实现(protothread)。linux上使用协程库的方式,通常也会分为替换函数和更为暴力的替换so来实现。当然而各种方式有各自的优劣。而st选用的汇编语言实现setjump&longjump和要求用户调用st_打头的函数来嵌入程序。所以st具备了跨平台的能力,以及让开发者们更开心的“与允许调用者自行选择切换时机”的能力。随着coroutine ts正式进入c++20,c++已经进入协程时代了。但是不幸的是c++20的协程标准只包含编译器需要实现的底层功能,并没有包含简单方便地使用协程的高级库,相关的类和函数进入std标准库估计要等到c++23。本文先以协程库(State Threads)来讲解c++中的协程。

2、state threads优缺点

优点:

  •        允许设计快速且高效可扩展的应用程序。应用程序将在CPU的负载和数量上都能很好的扩展。
  •        简化了应用程序的编程和调试,因为通常不需要互斥锁定,并且整个应用程序都可以自由适应静态变量和不可重入的库函数。

缺点:

  •        套接字上的所有I/O操作都必须适应st库的I/O函数,因为只有那些函数才能执行线程调度并阻止应用程序的进程被阻塞。

3、state threads工作流程

       在宏观的来看,ST的结构主要分成:

  • vp_schedule。主要是负责了一个调度的能力。有点类似于linux内核当中的schedule()函数。每次当这个函数被调用的时候,都会完成一次线程的切换。
  • 各种Queue。用于保存各种状态下等待被调度协程(st_thread)
  • Timer。用于记录各种超时和sleep。
  • poll。用于监听各种io事件,会根据系统能力不同而进行切换(kqueue、epoll、poll、select)。
  • st_thread。用于保存各种协程的信息。

其中比较重要的是schedule模块和thread模块两者。这两者实现了一个完整的协程切换和调度。属于st的核心。而schedule部分通常是开发者们最需要关心的部分。

 

二、具体实现

0、库的下载

https://sourceforge.net/projects/state-threads/

解压:tar zxvf st-1.9.tar.gz

编译:make linux-debug

解压并编译后的结果如下:

(st-1.9)解压后文件简介:

(1)doc目录

  • st.html——ST库概论
  • timeout_heap.txt——超时heap实现
  • notes.html——给出了编程注意点,包括移植,信号,进程内同步,进程间同步,非网络IO,超时处理,特别谈到进程内同步非常简单,不需要同步资源;非网络IO中谈到drawback和设计时需要避免的方法
  • reference.html——一个API接口文档介绍

(2)example目录

  • server:包含server.c和error.c。server接受一个客户端连接,接收客户端数据并返给客户端一个简单的HTML网页
  • lookupdns:包含lookupdns.c和res.c。
  • proxy:包含proxy.c。

st程序结构:

       st底层基于event-driven select/poll/kqueue/epoll等IO复用机制。下面以e

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是一个使用C++线程和协程的示例案例: ```cpp #include <iostream> #include <thread> #include <vector> #include <experimental/coroutine> using namespace std::experimental; struct coroutine_data { std::coroutine_handle<> coro; int thread_id; }; void task(std::vector<coroutine_data>& coroutines) { while (true) { for (auto& data : coroutines) { if (data.coro) { if (!data.coro.done()) { data.coro.resume(); } else { data.coro = nullptr; } } } } } struct coro_scheduler { struct promise_type { coro_scheduler* scheduler; coroutine_data data; auto get_return_object() { return coro_handle<promise_type>::from_promise(*this); } auto initial_suspend() { return suspend_never{}; } auto final_suspend() { return suspend_never{}; } void unhandled_exception() { std::terminate(); } void return_void() {} }; coro_scheduler(int num_threads) { for (int i = 0; i < num_threads; ++i) { threads.emplace_back(std::bind(task, std::ref(coroutines))); } } ~coro_scheduler() { for (auto& data : coroutines) { if (data.coro) { data.coro.destroy(); } } for (auto& thread : threads) { thread.join(); } } auto schedule(coroutine_data data) { coroutines.emplace_back(data); return coroutines.back().coro; } std::vector<std::thread> threads; std::vector<coroutine_data> coroutines; }; struct my_coroutine { struct promise_type { auto get_return_object() { return coro_handle<promise_type>::from_promise(*this); } auto initial_suspend() { return suspend_always{}; } auto final_suspend() { return suspend_always{}; } void unhandled_exception() { std::terminate(); } void return_void() {} }; my_coroutine(coro_scheduler& scheduler, int thread_id) : m_scheduler(scheduler), m_thread_id(thread_id) {} ~my_coroutine() {} void resume() { m_scheduler.schedule({m_handle, m_thread_id}); } void operator()() { std::cout << "Coroutine running on thread " << m_thread_id << std::endl; resume(); } coro_handle<promise_type> m_handle; coro_scheduler& m_scheduler; int m_thread_id; }; int main() { coro_scheduler scheduler(2); my_coroutine coro1(scheduler, 0); my_coroutine coro2(scheduler, 1); coro1.m_handle = coro1(); coro2.m_handle = coro2(); coro1.resume(); coro2.resume(); coro1.m_handle.destroy(); coro2.m_handle.destroy(); return 0; } ``` 在这个示例,我们定义了一个 `coroutine_data` 结构体来存储协程和它所在的线程的信息,以便在任务函数调度和执行协程。我们还实现了一个 `coro_scheduler` 类来管理协程的调度,并创建了一组线程来执行任务函数。 我们使用 `promise_type` 结构体来定义协程的返回类型,以及其初始化和终止时的行为。在 `my_coroutine` 类,我们定义了一个 `resume()` 函数,该函数将当前协程的句柄和所在的线程信息传递给调度器,并将协程挂起。在任务函数,我们检查每个协程的状态,并在需要时恢复它们的执行。 在 `main()` 函数,我们创建了两个 `my_coroutine` 实例并启动它们。我们还在每个协程的最后调用了 `destroy()` 来释放它们的资源。 总的来说,这个示例演示了如何使用 C++ 线程和协程来实现并发编程和任务调度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值