C++协程入门

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值