C++协程入门

本文介绍了C++中的协程概念,包括定义、优缺点。通过详细讲解基于ucontext的协程实现,展示了Coroutine类的设计与测试,以及基于Boost的Coroutine的快速切换特性。最后,通过一个排列数生成器的例子,进一步阐述协程在异步IO中的应用潜力。
摘要由CSDN通过智能技术生成

1 什么是协程

定义

  • 协程可以理解为用户态轻量级线程;
  • 协程拥有自己的上下文和栈;
  • 协程的切换和调度由用户定义,不用陷入内核;
  • 如同一个进程拥有多个线程,一个线程可以拥有多个协程。

优点

  • 极高的执行效率 因为协程切换不用陷入内核,是由用户程序定义切换逻辑,因此协程没有线程切换的开销。
  • 以同步代码的方式写异步逻辑 可开发异步IO。

缺点

由于协程是在单个线程内切换的,无法利用多核资源。结合多进程/多线程可以解决这个问题。

2 协程的实现

协程在一些脚本语言如Python、Lua中都已经很好地支持了(C++20也支持协程),但为了更好地学习它,还是有必要去逐步封装一个协程。本文主要利用Linux的ucontext库去封装一个Coroutine类,再与boost的基于fcontext的Coroutine库做一个对比。

2.1 基于ucontext的实现

ucontext.h 简介

头文件<ucontext.h>提供以下4个调用:

int getcontext(ucontext_t * ucp);
int setcontext(const ucontext_t *ucp);
void makecontext(ucontext_t *ucp, void(*func)(), int argc, ...);
int swapcontext(ucontext_t *oucp, ucontext_t *ucp);

以下逐个介绍它们:

  1. int getcontext(ucontext_t * ucp);
  • 获取当前上下文, 初始化ucp结构体, 将当前上下文保存到ucp中;
  • 成功不返回,失败返回-1, 并设置errno。
  1. void makecontext(ucontext_t *ucp, void(*func)(), int argc, ...);
  • 创建一个目标上下文 ,用于初始化一个协程,并且上下文需要
    • ucp来自getcontext调用;
    • 指定分配给上下文的栈uc_stack.ss_sp
    • 指定这块栈的大小uc_stack.ss_size,如32K, 64K, 128K;
    • 指定uc_stack.ss_flags,一般为0;
    • 指定后继上下文uc_link
  1. int setcontext(const ucontext_t *ucp);
  • 设置当前的上下文为ucp
  • ucp来自getcontext, 那么上下文恢复至ucp
  • ucp来自makecontext, 那么将会调用makecontext函数的第二个参数指向的函数func, 如果func返回, 则恢复至ucp->uc_link指定的后继上下文, 如果该ucp中的uc_linkNULL, 那么线程退出;
  • 成功不返回, 失败返回-1, 设置errno。
  1. int swapcontext(ucontext_t *oucp, ucontext_t *ucp);
  • 切换上下文
  • 保存当前上下文至oucp, 恢复ucp上下文(先执行makecontext指定的ucp入口函数, 而后会执行ucp->uc_link指向的后继上下文);
  • 成功不返回, 失败返回-1, 设置errno。
Coroutine类的实现

主要需要实现yield语义和resume语义,即协程主动让出控制权和恢复某个特定的协程。另外,需要定义4种状态,即INIT(初始)、RUNNING(运行)、SUSPEND(挂起)、TERM(结束),便于切换时检查协程的状态。由于协程是在线程内切换的,类似进程拥有一个主线程,线程内也应拥有一个主协程,用线程局部变量(thread_local)定义。

coroutine.h

#ifndef _COROUTINE_H_
#define _COROUTINE_H_
#include <memory>
#include <functional>
#include <ucontext.h>

class Coroutine : public std::enable_shared_from_this<Coroutine> {
   
public:
    typedef std::shared_ptr<Coroutine> ptr;
    typedef std::function<void()> Callback;
    enum State {
   
        INIT,          // 初始状态
        RUNNING,       // 运行中状态
        SUSPEND,       // 挂起状态
        TERM           // 结束状态
    };
    Coroutine(Callback cb, size_t stacksize = 0);
    
    ~Coroutine();
    uint64_t getId() {
    return id_; }
    State getState() {
    return state_; }
    void swapOut();
    void swapIn();
public:
    static void Resume(const Coroutine::ptr crt);
    static void Yield();
    static void SetThis(Coroutine* crt);
    static Coroutine::ptr GetThis();
    static void MainFunc();
private:
    Coroutine();
    uint64_t id_ = 0;
    uint32_t stacksize_ = 0;
    State state_ = INIT;
    ucontext_t ctx_;
    void* stack_ = nullptr; // 协程堆栈
    Callback cb_;

    struct Comparator {
   
        bool operator()(const Coroutine::ptr& lhs, const Coroutine::ptr& rhs) const;
    };
};
#endif

coroutine.cpp

#include "coroutine.h"
#include <assert.h>
#include 
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++协程是一种轻量级的线程,可以在单个线程中实现多个协程C++20引入了协程库,其中包括协程原语和一些实用程序,使协程的使用变得更加容易。下面是一个简单的C++协程实现demo: ```c++ #include <iostream> #include <coroutine> struct Generator { struct promise_type { int current_value; Generator get_return_object() { return Generator(std::coroutine_handle<promise_type>::from_promise(*this)); } auto initial_suspend() { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } void unhandled_exception() {} void return_void() {} auto yield_value(int value) { current_value = value; return std::suspend_always{}; } }; std::coroutine_handle<promise_type> coroutine; Generator(std::coroutine_handle<promise_type> h) : coroutine(h) {} ~Generator() { if (coroutine) coroutine.destroy(); } int get() { coroutine.resume(); return coroutine.promise().current_value; } }; Generator counter(int begin, int end) { for (int i = begin; i <= end; ++i) co_yield i; } int main() { auto gen = counter(1, 5); while (auto value = gen.get()) std::cout << value << ' '; } ``` 这个demo中,我们定义了一个生成器(Generator)结构体和一个promise_type结构体。Generator结构体包含一个协程句柄,可以通过该句柄来操作协程。promise_type结构体定义了生成器的类型,并提供了返回对象,挂起,终止等方法。在counter函数中,我们使用co_yield关键字来挂起当前协程并返回值。在主函数中,我们使用while循环来不断调用协程的get方法来获取生成的值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值