首页 > 其他分享 >协程库

协程库

时间:2024-02-26 14:36:16浏览次数:17  
标签:协程库 协程 func coroutine Coroutine brief context

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;
    /*
    协程状态:running运行中,ready就绪,term结束运行
    */
    enum State
    {
        // 就绪态,刚创建或者yield之后
        READY,
        // 运行态,resume之后的状态
        RUNNING,
        // 结束态,协程的回调函数执行完后的状态
        TERM
    };

private:
    /*
    @brief:构造函数
    只用于创建第一个协程,即线程主函数对应的协程
    只能由GetThis()方法调用,所以为私有
    */
    Coroutine();

public:
    /*
    @brief:构造函数,用于创建用户协程
    @param1 func 协程入口函数
    @param2 stacksize 栈大小
    @param3 run_in_scheduler 本协程是否参与调度器调度,默认true
    */
    Coroutine(std::function<void()> func, size_t stacksize = 0, bool run_in_scheduler = true);

    /*
    @brief:析构函数
    */
    ~Coroutine();

    /*
    @brief: 重置协程状态和入口函数,复用已经存在的栈空间
    @param1 func
    */
    void reset(std::function<void()> func);

    /*
    @brief: resume,将当前协程切换到执行状态
    @details 当前协程和正在运行的协程进行交换,
    当前协程状态变为running,正在运行的协程状态变成ready
    */
    void resume();

    /*
    @brief yield让出执行权
    @details 当前协程和上次退到后台的协程进行交换,
    当前协程状态变为ready,后台协程变为running
    */
    void yield();

    /*
    @brief 获取协程id
    */
    uint64_t getId() const { return c_id; };

    /*
    @brief 获取协程状态
    */
    State getState() const { return c_state; };

public:
    /*
    @brief 设置当前正在运行的协程,设置线程局部变量t_coroutine的值
    */
    static void SetThis(Coroutine *crt);

    /*
    @brief 返回当前线程正在执行的协程
    @details 如果当前线程还没有创建协程,则创建第一个协程,
    且该协程为当前线程的主协程,为调度协程,可以调度管理别的线程
    其他协程结束时,需要切回到主协程,由主协程选择别的协程resume
    @attention 线程如果要创建协程,需要首先执行GetThis函数,以此初始化主协程
    */
    static Coroutine::ptr GetThis();

    /*
    @brief 返回总协程数
    */
    static uint64_t TotalCoroutines();
    /*
    @brief 获取当前协程id
    */
    static uint64_t GetCoroutineId();
    /*
    @brief 协程⼊⼝函数
    */
    static void MainFunc();
   

    // 成员变量
private:
    // 协程id
    uint64_t c_id = 0;
    // 协程栈大小
    uint32_t c_stacksize = 0;
    // 协程栈地址
    void *c_stack = nullptr;
    // 协程状态
    State c_state = READY;
    // 协程上下文
    ucontext_t c_context;
    // 协程执行函数
    std::function<void()> c_func;
    // 协程是否参与调度器调度
    bool c_runInScheduler;
};

Coroutine.cpp

#include "Coroutine.h"
#include <atomic>
#include <string.h>
#include <assert.h>
#include <iostream>

// 默认栈大小
#define DEFAULT_STACK_SIZE 1024 * 128

// 全局静态变量,用于生成协程id
static std::atomic<uint64_t> s_coroutine_id{0};
// 全局静态变量,用于统计当前协程数量
static std::atomic<uint64_t> s_coroutine_count{0};

// 线程局部变量,当前线程正在运行的协程
static thread_local Coroutine *thread_coroutine = nullptr;
// 用于保存当前协程库中的主协程,即调度协程
static thread_local Coroutine::ptr main_coroutine = nullptr;

Coroutine::Coroutine()
{
    // 设置当前协程为运行协程,因为改构造函数只用来创建第一个协程,所以状态一定为running
    SetThis(this);
    c_state = RUNNING;

    // 成功时返回0
    // 失败了返回-1
    if (getcontext(&c_context))
    {
        perror("getcontext failed...");
    }

    // 当前协程id和协程数量自增
    ++s_coroutine_count;
    c_id = s_coroutine_id++;

    std::cout << "Coroutine() id : " << c_id << std::endl;
}

/*
    @brief:构造函数,用于创建用户协程
    @param1 func 协程入口函数
    @param2 stacksize 栈大小默认128k
    */
Coroutine::Coroutine(std::function<void()> func, size_t stacksize, bool run_in_scheduler)
    : c_id(s_coroutine_id++), c_func(func), c_runInScheduler(run_in_scheduler)
{
    ++s_coroutine_count;

    // 分配协程栈空间
    c_stacksize = stacksize ? stacksize : DEFAULT_STACK_SIZE;
    c_stack = malloc(c_stacksize);
    if (c_stack == nullptr)
    {
        perror("malloc stack failed...");
    }
    // c_stack = new char[c_stacksize];
    memset(c_stack, 0, c_stacksize);

    // 获取上下文
    if (getcontext(&c_context))
    {
        perror("getcontext failed...");
    }
    // 设置上下文
    c_context.uc_stack.ss_sp = c_stack;
    c_context.uc_stack.ss_size = c_stacksize;
    c_context.uc_link = nullptr;

    makecontext(&c_context, &Coroutine::MainFunc, 0);

    std::cout << "Coroutine() id : " << c_id << std::endl;
}
/*
@brief 析构函数
*/
Coroutine::~Coroutine()
{
    --s_coroutine_count;
    // 主协程由无参构造函数创建,没有对应的栈
    if (c_stack)
    {
        assert(c_state == TERM);
        free(c_stack);
    }
    else // 主协程
    {
        assert(!c_func);
        assert(c_state == RUNNING);
        Coroutine *cur = thread_coroutine;
        if (cur == this)
        {
            SetThis(nullptr);
        }
    }
    std::cout << "~Coroutine() id : " << c_id << std::endl;
}

/*
    @brief 返回当前线程正在执行的协程
    @details 如果当前线程还没有创建协程,则创建第一个协程,
    且该协程为当前线程的主协程,为调度协程,可以调度管理别的线程
    其他协程结束时,需要切回到主协程,由主协程选择别的协程resume
    @attention 线程如果要创建协程,需要首先执行GetThis函数,以此初始化主协程
    */
Coroutine::ptr Coroutine::GetThis()
{
    if (thread_coroutine)
    {
        return thread_coroutine->shared_from_this();
    }
    // 创建第一个协程
    Coroutine::ptr first_coroutine(new Coroutine);
    assert(thread_coroutine == first_coroutine.get());
    main_coroutine = first_coroutine;
    return thread_coroutine->shared_from_this();
}

/*
@brief 设置当前正在运行的协程,设置线程局部变量t_coroutine的值
*/
void Coroutine::SetThis(Coroutine *crt)
{
    thread_coroutine = crt;
}

/*
    @brief: resume,将当前协程切换到执行状态
    @details 当前协程和正在运行的协程进行交换,
    当前协程状态变为running,正在运行的协程状态变成ready
    */

void Coroutine::resume()
{
    // resume的协程不能是终止的或者是正在运行的
    assert(c_state != TERM && c_state != RUNNING);
    // 设置当前协程为this
    SetThis(this);
    c_state = RUNNING;
    // 由于实现的是非对称协程,所有协程只能由主协程进行调控,所以这里保存的上下文是main_coroutine的上下文
    if (swapcontext(&main_coroutine->c_context, &c_context))
    {
        perror("resume swapcontext failed...");
    }
}

/*
    @brief yield让出执行权
    @details 当前协程和上次退到后台的协程进行交换,
    当前协程状态变为ready,后台协程变为running
    */
void Coroutine::yield()
{
    // 运行完成后会自动yield此时为term状态
    assert(c_state == RUNNING || c_state == TERM);
    // 回到主协程
    SetThis(main_coroutine.get());
    if (c_state != TERM)
    {
        c_state = READY;
    }
    // 上下文切换
    if (swapcontext(&c_context, &main_coroutine->c_context))
    {
        perror("yield swapcontext failed...");
    }
}

/*
    @brief 协程⼊⼝函数
    */
void Coroutine::MainFunc()
{
    Coroutine::ptr cur = GetThis();
    assert(cur);

    cur->c_func(); // 这里进行协程的执行,真正的入口函数
    cur->c_func = nullptr;
    cur->c_state = TERM;
    //这里的解释
    /*
    这里为什么要使用裸指针调用yield()而不是使用cur来调用呢?
    如果使用cur的话,指针一直在栈上引用计数一直为1,无法正常进行释放
    */
    auto raw_ptr = cur.get();
    cur.reset();
    /*
    一个函数执行结束后这个ucontext_t会自动根据其中uclink寻找回去的路
    但是作为一个非对称的协程库,uclink其实不用设置(本项目中uclink都是nullptr)
    因为每个协程执行完都要回到主协程,所以直接调用yield()让他切回主协程就行了
    */
    raw_ptr->yield(); 
}
/*
    @brief: 重置协程状态和入口函数,复用已经存在的栈空间
    @param1 func
    */
void Coroutine::reset(std::function<void()> func)
{
    // 主协程是没有栈的,只复用子协程
    assert(c_stack);
    assert(c_state == TERM);
    c_func = func;
    if (getcontext(&c_context))
    {
        perror("reset getcontext failed...");
    }
    c_context.uc_link = nullptr;
    c_context.uc_stack.ss_sp = c_stack;
    c_context.uc_stack.ss_size = c_stacksize;

    makecontext(&c_context, &Coroutine::MainFunc, 0);

    c_state = READY;
}

uint64_t Coroutine::TotalCoroutines()
{

    return s_coroutine_count;
}
uint64_t Coroutine::GetCoroutineId()
{
    if (thread_coroutine)
    {
        return thread_coroutine->getId();
    }
    return 0;
}

简单的测试程序

#include "Coroutine.h"
#include <iostream>

using namespace std;


void runInCoroutine()
{
    cout<<"RunInCoroutine start"<<endl;
    Coroutine::GetThis()->yield();
    cout<<"RunInCoroutine end"<<endl;
}

int main()
{
    Coroutine::GetThis();
    cout<<"Main start"<<endl;
    Coroutine::ptr crt(new Coroutine(runInCoroutine));
    crt->resume();
    cout<<"Main after resume"<<endl;
    crt->resume();
    cout<<"Main end"<<endl;

}

标签:协程库,协程,func,coroutine,Coroutine,brief,context
From: https://www.cnblogs.com/liviayu/p/18034242

相关文章

  • asyncio协程库实现原理剖析
    目录迭代器可迭代对象&迭代器自定义迭代器生成器生成器就是迭代器生成器的4个状态用yield重构迭代器生成器三种应用场景生成器进阶函数运行机制函数对象和代码对象函数运行帧同步和异步协程yield表达式yield表达式的优先级send()用法yield表达式作为函数入参使用close()结束生成......
  • 【并发编程二十】协程(coroutine)_协程库
     【并发编程二十】协程(coroutine)一、线程的缺点二、协程三、优点四、个人理解五、协程库1、window系统2、unix系统(包括linux的各个版本)2.1、makecontext2......
  • 协程库的一些笔记
    因为协程的好处,所以协程库现在有好多libtask,boost::coroutine,libco...... libtask很不错,以后或许会用。 boost我个人基本很少用。 腾讯的libco自己用汇编实现了s......