协程的问题
- 为什么要有协程?
- 协程的原语操作?
- 协程的切换?
- 协程的struct如何定义?
- 协程的scheduler(调度)如何定义?
- 调度策略如何实现?
- 协程如何与posix,api兼容?
- 协程多核模式?
- 协程的性能如何测试?
为什么要有协程
同步的编程方式,异步的性能。同步编程时,我们需要等待io就绪。但是在协程这里,我们使用一种机制,当io需要等待时,就切到下一个io,之后当之前的io就绪时,再切换回来继续处理就绪事件。
IO 异步操作与 IO 同步操作
同步/异步是用来形容两者之间的关系的
举个例子:这里有个dns服务器,还有一个dns客户端,假如我们向dns服务器发送50个请求,也就是我们发送发送50个域名,然后dns要返回50个ip地址。
同步就是我们向dns服务器请求一个,然后dns服务器返回一个。也就是这50个事串行的。
异步就是我们向dns服务器请求很多个,然后dns服务器有结果了就返回。也就是这50个事并行的。
从代码上看:
同步
sync(){send(request);recv(response);
}
response_cb(){//回调函数recv();
}
async(){send(request,response_cb);
}
异步有个缺点,send()和response()不是在一个流程里面,他可能有一个线程,或者有一个while()循环一直监视着是否可以调用response。所以可能send()和response()不是在一个线程里面。如果response中再调用回调的话,那么这个代码非常不利于人的阅读。
对比项 | IO 同步操作 | IO 异步操作 |
---|---|---|
代码逻辑 | 程序整体逻辑清晰 | 子模块逻辑清晰 |
程序性能 | 响应时间长,性能差 | 响应时间短,性能好 |
所以同步编程简单,易于阅读。异步编程复杂,不太利于人的阅读。
再例如:检测io,读写,再一个流程里面。这里的同步和异步是说的检测io和读写io之间的关系。同步的性能是不如异步的。
7300ms,同步
while(1){epoll_wait();for(){recv();send();}
}
1600ms,异步
while(1){epoll_wait();for(){push_thread();}
}
所以同步编程简单,易于阅读。异步编程复杂,不太利于人的阅读。
所以有没有一种方式编程方式,拥有同步的编程方式,异步一样的性能。
协程就是这样的,我们写代码的时候看着是同步的,但是他底层是异步的。
async_Recv(fd,buffer,length){ret=poll(fd);//是否就绪epoll_ctl(add);//就是就绪了之后能切换回来。switch(next_fd);//不就绪就切换
}
async_Send(fd,buffer,length){ret=poll(fd);//是否就绪epoll_ctl(add);switch(next_fd);//不就绪就切换
}while(1){epoll_wait();for(){async_Recv(fd,buffer,length);parse(buffer);async_Send(fd,buffer,length);}
}
就是处理的时候,如果没有就绪就切换到下一个,然后将当前这个加入到epoll中,如果这个就绪了,方便下次处理这个。
这里的切出用yield,回来用resume.
协程的原语操作
协程的核心原语操作:create, resume, yield。
两个,一个让出,一个切换回来
yield(from,to);//这个是切出
resume(to,from);//这个是回来
create:创建一个协程。
- 调度器是否存在,不存在也创建。调度器作为全局的单例。将调度
器的实例存储在线程的私有空间 pthread_setspecific。 - 分配一个 coroutine 的内存空间,分别设置 coroutine 的数据
项,栈空间,栈大小,初始状态,创建时间,子过程回调函数,子
过程的调用参数。 - 将新分配协程添加到就绪队列 ready_queue 中
yield: 让出 CPU。
void nty_cor