C++libco协程

本文深入介绍了协程的概念,包括其与线程的区别、在单核和多核环境下的执行效率,并强调了协程在异步编程中的优势。C++20引入了协程支持,而腾讯开源的libco库提供了易用的协程编程接口,简化了异步改造。通过生产者消费者问题的示例展示了libco的使用,强调了其无需侵入业务逻辑即可实现高并发的能力。
摘要由CSDN通过智能技术生成

什么是协程?

协程:协同运行的子程序(函数);
协程:协同运行的进程(shell中的概念)

协程是能暂停执行以在之后恢复的函数。协程是无栈的:它们通过返回到调用方暂停执行,并且从栈分离存储恢复所要求的数据。这允许编写异步执行的顺序代码(例如不使用显式的回调来处理非阻塞 I/O),还支持对惰性计算的无限序列上的算法及其他用途。
协程可以被视为提供特殊控制流的语言级构造。
与抢占式线程相比,协程开关是协作的(程序员控制开关何时发生)。协程开关中不涉及内核。

单核情况下,线程和协程都是不能并行的,但是谁快?是协程更快,因为协程有更少的上下文切换,而且, 同一个线程内的不同协程之间的切换由用户态完成,而线程的切换需要内核态的cpu去保存上下文环境,所以协程更快。
多核的情况下,一个线程只能被一个cpu执行,那么当前,一个线程里的多个协程只能是并发的,不能是并行的,而多个线程下的多个协程就可以是并行的,并且速度也是更快。

协程可以干什么?

当一个函数阻塞线程的时候,我们为了将同步改为异步,我们一般会选择多线程或者使用回调机制来将同步改成异步,用的最多的就是IO多路复用,但是,协程,既没有用线程,也没有用IO多路复用技术,就能实现不同函数之间的异步执行,从来提升了效率,在不使用线程,即没有上下文切换的负担下完成了高并发。

比如,当做IO的时候,我们可以把当前的线程thread_yield出让调度器,去执行别的线程,等再次调度到这个线程的时候再继续执行,这样会有一次上下文切换,比较浪费时间,所以诞生了IO多路复用,它类似Qt的信号槽机制,类似信号,IO进行的时候,我就去做别的事情,等io做完了,给我一个信号,或者给我一个信息,我执行相应的函数。

C++20已经提出了协程的概念,并完成了底层函数,但是没有提供标准库。
Libco为腾讯开源的C++常用的协程库。
libco通过仅有的几个函数接口 co_create/co_resume/co_yield 再配合 co_poll,可以支持同步或者异步的写法,如线程库一样轻松。同时库里面提供了socket族函数的hook,使得后台逻辑服务几乎不用修改逻辑代码就可以完成异步化改造。、

我一直感觉协程和IO多路复用技术很像,有点分不清,网传了一篇文章,说实际在业务上的IO多路复用,常常因为需要保存事件前后的状态而变得复杂,而协程恰恰可以自动保存这些状态,所以说IO多路复用,如果能和协程结合起来,会有奇效,不过这估计也只有到了实际业务,有大佬带着才好写了,没有经验的估计搞不出来。

libco的特性

  • 无需侵入业务逻辑,把多进程、多线程服务改造成协程服务,并发能力得到百倍提升;
  • 支持CGI框架,轻松构建web服务(New);
  • 支持gethostbyname、mysqlclient、ssl等常用第三库(New);
  • 可选的共享栈模式,单机轻松接入千万连接(New);
  • 完善简洁的协程编程接口 类pthread接口设计,通过co_create、co_resume等简单清晰接口即可完成协程的创建与恢复;
  • __thread的协程私有变量、协程间通信的协程信号量co_signal (New);
  • 语言级别的lambda实现,结合协程原地编写并执行后台异步任务 (New);
  • 基于epoll/kqueue实现的小而轻的网络框架,基于时间轮盘实现的高性能定时器;

libco的安装

libco源码可以在腾讯开源里面找到,连接的GitHub上也有安装步骤,文件很少,直接make就可以,或者用cmake构建。
之后把.h文件cp到 /usr/include文件里,把solib文件夹里的动态库文件cp到/usr/lib64/下。

生产者消费者例子

// 使用生产者消费者问题学习协程
#include <co_routine.h>
#include <stdlib.h>
#include <time.h>
#include <vector>
#include <string>
#include <iostream>
// 定义消息内存
struct stPara_t
{
    // 条件变量
    stCoCond_t *cond;
    // 数据池
    std::vector<int> vecData;
    // 数据ID
    int id;
    // 协程ID,每个协程保存副本?
    int cid;
};

// 生产者函数
void* Producer(void *arg);
// 消费者函数
void* Consumer(void *arg);



int main()
{
    // 定义数据池
    stPara_t p;
    p.cond = co_cond_alloc();
    p.cid = 0;
    p.id = 0;

    // 设置随机数的种子,使用time提供的时间来初始化种子,保证能得到比较随机的种子
    srand(time(NULL));

    // 创建协程对象,一个生产者,两个消费者
    stCoRoutine_t *pProducerCo = nullptr;
    stCoRoutine_t *pConsumerCo[2] = {nullptr};

    // 创建生产者协程
    // 调用函数创建协程对象,函数内分配对象指针
    // 此函数不会报错
    /**
     * 1.出参,返回创建的协程对象
     * 2.入参,指定创建协程的属性,默认属性为空
     * 3.入参,指定协程执行的函数
     * 4.入参,执行函数的参数
     */
    co_create(&pProducerCo,NULL,Producer,&p);
    // 启动协程
    co_resume(pProducerCo);
    std::cout << "start producer coroutine success" << std::endl;

    // 创建消费者协程
    for (int i = 0; i < 2; i++)
    {
        co_create(&pConsumerCo[i],NULL,Consumer,&p);
        co_resume(pConsumerCo[i]);
    }
    std::cout << "start consumer coroutine success" << std::endl;

    // 启动循环事件,执行epoll的事件循环处理,那么不就是底层调用了epoll么?
    co_eventloop(co_get_epoll_ct(),NULL,NULL);

    exit(0);
}

void* Producer(void *arg)
{
    // 启用协程hook(钩子)
    co_enable_hook_sys();

    stPara_t *p = (stPara_t *)arg;
    int cid = ++p->cid;
    while (true)
    {
        // 产生随机个数据
        for (int i = rand()%5 + 1; i > 0; --i)
        {

            p->vecData.push_back(++p->id);
            std::cout << cid << " cid add new data :" << p->id << std::endl;
        }
        // 通知消费者
        co_cond_signal(p->cond);
        // 必须使用poll等待,不然没法切换协程
        // 第三个参数是等待的时间,毫秒,哪怕是1ms,消费者协程都可以执行完毕
        poll(NULL,0,1);
    }
    return NULL;
}

void* Consumer(void *arg)
{
    // 启动协程hook
    co_enable_hook_sys();

    stPara_t *p = (stPara_t *)arg;
    int cid = ++p->cid;
    while (true)
    {
        // 检查数据池,无数据则等待通知
        if(p->vecData.empty())
        {
            co_cond_timedwait(p->cond,-1);
            continue;
        }
        // 消费数据
        std::cout << cid << " cid del data:" << p->vecData.front() << std::endl;
        p->vecData.erase(p->vecData.begin());
    }
    return NULL;
    
}

makefile

CFLAGS+=-g -Wall
LDFLAGS+= -L/home/adce/libco/libco-master/lib	# 静态库.a文件的路径
LDLIBS+=  -lstdc++ -lcolib -pthread -ldl

all:libco1
libco1:libco1.o
	$(CC) $^ -o $@ $(CFLAGS) $(LDFLAGS) $(LDLIBS)

clean:
	rm -rf *.o libco1

运行结果

在这里插入图片描述

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙子砰砰枪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值