目录
什么是协程
我们可以简单的认为:协程就是用户态的线程,但是上下文切换的时机是靠调用方(写代码的开发人员)自身去控制的;
同时,协程和用户态线程非常接近,用户态线程之间的切换不需要陷入内核,但部分操作系统中用户态线程的切换需要内核态线程的辅助;
比如下面是一个简单的例子:
void A() {
cout << 1 << " ";
cout << 2 << " ";
cout << 3 << " ";
}
void B() {
cout << "x" << " ";
cout << "y" << " ";
cout << "z" << " ";
}
int main(void) {
A();
B();
}
显然,单线程中函数输出1 2 3 x y z
如果用libco库(c++的协程库)把这个改造一下
void A() {
cout << 1 << " ";
cout << 2 << " ";
co_yield_ct(); // 切出到主协程
cout << 3 << " ";
}
void B() {
cout << "x" << " ";
co_yield_ct(); // 切出到主协程
cout << "y" << " ";
cout << "z" << " ";
}
int main(void) {
... // 主协程
co_resume(A); // 启动协程 A
co_resume(B); // 启动协程 B
co_resume(A); // 从协程 A 切出处继续执行
co_resume(B); // 从协程 B 切出处继续执行
}
则会得到1 2 x 3 y z
切出跳出操作,协程中称之为yield 恢复和启动则称之为resume
那么为什么使用协程而不是多线程?
因为线程的操作太重量级了,而在当前的情况下,我们只是希望先暂停当前任务,等待io好了之后再切回来
协程的优缺点:
- 减少了线程的重复高频创建;
- 尽量避免线程的阻塞;
- 让原来要使用异步+回调方式写的非人类代码,可以用看似同步的方式写出来,提升代码的可维护与可理解性(毕竟不需要考虑多线程那一套东西了)
缺点:
⽆法利⽤多核资源。线程才是系统调度的基本单位,单线程下的多协程本质上还是串⾏执⾏的,只能⽤到单核
计算资源,所以协程往往要与多线程、多进程⼀起使⽤。
协程的分类
对称协程与⾮对称协程
对称协程,协程可以不受限制地将控制权交给任何其他协程。任何⼀个协程都是相互独⽴且平等的,调度权可以在任意协程之间转移
⾮对称协程,是指协程之间存在类似堆栈的调⽤⽅-被调⽤⽅关系。协程出让调度权的⽬标只能是它的调⽤者。
有栈协程与⽆栈协程
有栈协程:⽤独⽴的执⾏栈来保存协程的上下⽂信息
⽆栈协程:它不需要独⽴的执⾏栈来保存协程的上下⽂信息,协程的上下⽂都放到公共内存中,当协程被挂起时,
⽆栈协程会将协程的状态保存在堆上的数据结构中,并将控制权交还给调度器。