安装
https://github.com/Tencent/libco 上把release版本的下下来:
mkdir build && cd build && cmake .. && make
拿到动态和静态库啦,然后cp到/usr/local/lib就完成安装啦。
项目有很多example,直接进根目录make就行了。
使用单线程多协程实现生产者消费者
说明:这个例子里面没有加锁,大多数情况,单线程多协程无需加锁,但是有些情况需要,比如两个协程同时写一个文件。下面这个生产者消费者模型不需要加锁,因为它操作的共享变量是安全的,而在多线程模型下,调度是抢占的,由系统内核发起的,需要加锁,而协程是用户级别的模拟并发。协程的优势在于切换协程带来的花销远小于线程切换,线程切换需要陷入内核,切换寄存器和栈,协程则不需要。在多线程编码时,条件变量需要和锁一起用,而在协程编程中,不需要这种和锁搭配的条件变量,libco实现了一个条件变量stCoCond_t
。
使用libco步骤:
- 写协程函数
void*(*p)(void*)
,入口写co_enable_hook_sys();
启用协程HOOK项。 - 声明协程
stCoRoutine_t
。 - 创建协程
co_create(&pProducerCo, NULL, productor, &fac);
- 开启协程
co_resume(pProducerCo);
- 开启事件循环
co_eventloop(co_get_epoll_ct(), NULL, NULL);
编译下面这个demo:
g++ -o main main.cc -lcolib -lpthread -ldl -g
或者
g++ -o main -Wl,-Bstatic main.cc -lcolib -Wl,-Bdynamic -lpthread -ldl -g
由于我lib目录下有同名的libco静态和动态库,g++会默认使用动态库,如果想用静态库编译,使用第二个编译命令。另外 -ldl
是必须的,貌似colib内部会加载一些基础动态库。
#include "co_routine.h"
#include <iostream>
#include <queue>
#include <functional>
// g++优先使用同名动态库
// g++ -o main main.cc -lcolib -lpthread -ldl -g
// 使用同名静态库编译
// g++ -o main -Wl,-Bstatic main.cc -lcolib -Wl,-Bdynamic -lpthread -ldl -g
int task = 0;
using namespace std;
class Factory {
public:
Factory() {
maxElement_ = 5;
proCond_ = co_cond_alloc();
conCond_ = co_cond_alloc();
}
stCoCond_t* proCond_;
stCoCond_t* conCond_;
queue<int> taskQ_;
int maxElement_;
};
void* productor(void* arg) {
// 启用协程HOOK项
co_enable_hook_sys();
Factory* fac = static_cast<Factory*>(arg);
while (true) {
while (fac->taskQ_.size() >= fac->maxElement_) {
co_cond_timedwait(fac->proCond_, -1);
}
fac->taskQ_.push(task++);
cout << "生产任务:" << fac->taskQ_.back() << endl;
co_cond_signal(fac->conCond_);
}
}
void* consumer(void* arg) {
// 启用协程HOOK项
co_enable_hook_sys();
Factory* fac = static_cast<Factory*>(arg);
while (true) {
while (fac->taskQ_.empty()) {
co_cond_timedwait(fac->conCond_, -1);
}
int task = fac->taskQ_.front();
fac->taskQ_.pop();
cout << "消费任务:" << task << endl;
co_cond_signal(fac->proCond_);
}
}
int main(int argc, char** argv) {
const int nConsumer = 2;
stCoRoutine_t* pProducerCo = NULL;
stCoRoutine_t* pConsumerCo[nConsumer] = { NULL };
Factory fac;
// 创建启动生产者协程
// 看源码可知该函数必返回0
co_create(&pProducerCo, NULL, productor, &fac);
co_resume(pProducerCo);
cout << "start producer coroutine success" << endl;
// 创建启动消费者协程
for (int i = 0; i < nConsumer; i++)
{
co_create(&pConsumerCo[i], NULL, consumer, &fac);
co_resume(pConsumerCo[i]);
}
cout << "start consumer coroutine success" << endl;
// 启动循环事件
co_eventloop(co_get_epoll_ct(), NULL, NULL);
return 0;
}
消费任务:41253
消费任务:41254
生产任务:41255
生产任务:41256
生产任务:41257
生产任务:41258
生产任务:41259
消费任务:41255
消费任务:41256
消费任务:41257
消费任务:41258
消费任务:41259
生产任务:41260
生产任务:41261
生产任务:41262
生产任务:41263
生产任务:41264
消费任务:41260
消费任务:41261
消费任务:41262
消费任务:41263
消费任务:41264
生产任务:41265
生产任务:41266
生产任务:41267
生产任务:41268
生产任务:41269
消费任务:41265
只有一个线程,佐证了协程的含义。