简单的协程库,有以下特性:
- 非对称式+有栈协程
- 适用于
Linux x86_64
环境 - 提供友好的函数调用方式、闭包和协程检测机制
header-only
支持- 协程化的
POSIX
接口适配
接口很简单
每个线程下的coroutine
环境都需要co::open()
获取
co::open()
操作可以重复调用,仅会在首次调用时初始化环境
上述co::open()
将返回一个co::Environment&
实例
你可以通过它来创建协程,创建方式和std::thread
的构造函数差不多,接受任意可调用对象
比如有一个函数接口为print(int, char)
,可以这么写
auto coroutine = environment.createCoroutine(print, 1, 'a')
返回类型为std::shared_ptr<co::Coroutine>
注意创建好的协程co::Coroutine
并不会立刻启动
不管你是启动一个协程,还是恢复协程,都要coroutine.resume()
让出自身的协程上下文
只允许当前在运行的协程让出,既co::this_coroutine::yield()
co::test()
返回一个bool
,表示当前执行的控制流是否位于协程上下文
这个有什么用?看你发挥
比如有栈协程本身栈空间就是比较小的(相比完整的线程),但是堆分配本身又很慢
那么在一些空间吃紧的场合下可以用test()
来区分,实现协程上下文使用堆分配,线程/进程上下文使用栈分配
这需要应用层自己去实现
#include <iostream>
#include <memory>
#include <vector>
#include "co.hpp"
void where() {
std::cout << "running code in a "
<< (co::test() ? "coroutine" : "thread")
<< std::endl;
}
void print1() {
std::cout << 1 << std::endl;
co::this_coroutine::yield();
std::cout << 2 << std::endl;
}
void print2(int i, co::Coroutine *co1) {
std::cout << i << std::endl;
co1->resume();
where();
std::cout << "bye" << std::endl;
}
int main() {
auto &env = co::open();
auto co1 = env.createCoroutine(print1);
auto co2 = env.createCoroutine(print2, 3, co1.get());
co1->resume();
co2->resume();
where();
return 0;
}
// 1
// 3
// 2
// running code in a coroutine
// bye
// running code in a thread
co
支持POSIX
风格的接口:比如用co::read
替代系统调用read
其目的是以直观、同步的写法来完成异步事件,很轻松地完成高并发的服务器和客户端
目前已支持:
co::read
co::write
co::accept4
co::connect
co::sleep
co::usleep
co::poll
示例可以看test_posix
前缀的文件(服务端和客户端),仅要求fd
为NONBLOCK
形式
原理还是控制流的切换,并且搭配epoll
来作为一个隐藏的调度器
使用co::poll
可以定制每一个读写操作的超时时间,方便进行异常处理
并且允许co::poll(nullptr, 0, timeout)
直接作为定时器使用
但是对于简单的定时任务更加建议用co::usleep()
或co::sleep()
目前针对协程没有足够方便的事件通知机制,以降低生产者-消费者模型的编写难度,但是可以简单处理
如果只是一对一,可以用pipefd
结合co::read
即可,主动通知事件直接写1字节进去
如果是一对多或者多对多,可以自己写一个简单的wait / notify
(见示例)
或者干脆用co::usleep
等接口,消费者定时轮询,不需要生产者做任何事情
作为比较的库有:
- 作为未来标准的
boost::asio
- 奇虎360基于
libevent
和muduo
定制的evpp
co
用到的bench文件在这里,其它库使用的bench文件在这里
表中结果的单位为MiB/s
(threads / sessions) \ server | boost asio | qihoo360 evpp | co server |
---|---|---|---|
2 / 10 | 2981.57 | 3264.58 | 4096.6 |
2 / 100 | 2467.58 | 2090.98 | 2998.5 |
2 / 1000 | 1829.49 | 1585.79 | 1919.79 |
4 / 10 | 1079.29 | 631.259 | 4086.21 |
4 / 100 | 2948.08 | 2350.77 | 4207.58 |
4 / 1000 | 2009.47 | 1911.82 | 2200.29 |
8 / 10 | 590.297 | 766.125 | 788.441 |
8 / 100 | 2238 | 2432.45 | 3425.91 |
8 / 1000 | 1877.09 | 1866.43 | 2049.31 |
关于这个协程库,它的实现方式和微信libco是一致的,感兴趣的话可以看下我写的libco关键源码剖析(Caturra/RTFSC/libco)
另外我对云风大佬的coroutine也挺感兴趣,他的做法恰好是和这个库相反的对称式协程加上共享栈模式,有时间也可以试着实现一下
不过在这之前,我还是想想,该如何把我之前写的网络库进行协程化改造(第三版coming soon!)