测试文件
#include "coroutine.h"
#include "win_exit.h"
#include <stdio.h>
#include <thread>
void foo()
{
printf("function pointer\n");
}
struct A {
void fA() { printf("std::bind\n"); }
void fB() { printf("std::function\n"); }
};
int main()
{
//----------------------------------
// 使用关键字go创建协程, go后面可以使用:
// 1.void(*)()函数指针, 比如:foo.
// 2.也可以使用无参数的lambda, std::bind对象, function对象,
// 3.以及一切可以无参调用的仿函数对象
// 注意不要忘记句尾的分号";".
go foo;
go []{
printf("lambda\n");
};
go std::bind(&A::fA, A());
std::function<void()> fn(std::bind(&A::fB, A()));
go fn;
// 也可以使用go_stack创建指定栈大小的协程
// 创建拥有10MB大栈的协程
go co_stack(10 * 1024 * 1024) []{
printf("large stack\n");
};
// 创建单独线程启动调度器
std::thread t([]{ co_sched.Start(); });
t.detach();
while(true);
return 0;
}
上面一共用到了三种方式创建协程:
- 普通函数
- lambda
- 成员函数
- 使用function
go关键字是什么东西
作者是把go这个关键字抽象成了什么,可以这样创建线程,指定协程的函数
其实就是作者创建了一个结构体
__go,然后定义了减号运算符函数,参数接受的都是函数指针类型. 所以,go foo;这个代码就相当于:
__goo(文件名, 行号) - foo;
libgo协程的结构
协程调度器结构
-
Scheduler负责管理Dispatcher和Processer,默认会提供一个Scheduler名为co_sched. 协程只会在所属的调度器中被调度,创建额外的调度器可以实现业务间的隔离 -
processer负责协程调度,每一个processer维护了4个队列
newQueue_: 队列存放新加入的协程,包括新创建的协程,唤醒挂起的协程,还有 steal(偷) 来的协程runnableQueue_: 可运行协程队列waitQueue_: 存放挂起的协程gcQueue: 存放需要gc的协程
Dispatcher负责不同processer上的协程的负载均衡,实现协程的跨线程迁移steal, 增加新的processer等. 只有在processer数量大于1的时候才会创建Dispatcher线程