协程杂记
- 与线程不同,协程是自己主动让出CPU,并交付他期望的下一个协程运行,而不是在任何时候都有可能被系统调度打断。因此协程的使用更加清晰易懂,并且多数情况下不需要锁机制。
- 与线程相比,协程的切换由程序控制,发生在用户空间而非内核空间,因此切换的代价非常小。
https://blog.csdn.net/plutus_sutulp/article/details/8464167
事实上,libco 最大的特点就是将系统中的关于网络操作的阻塞函数全部进行相应的非侵入式改造,例如对于 read
,write
函数,libco 均定义了自己的版本,然后通过 LD_PRELOAD
进行运行时地解析,从而来达到阻塞时自动让出协程,并在 IO 事件发生时唤醒协程的目的。关于 libco 的整个架构和流程,后续会有详细介绍。
http://kaiyuan.me/2017/05/03/function_wrapper/
链接:https://www.zhihu.com/question/321006574
第一部分是,想想调度器怎么知道你的协程线程携程还是什么程阻塞在IO上了?或者说,调度器怎么知道你的线程在等待?
肯定不能靠看占多少CPU对吧,低CPU占用不等于在等吧。肯定是靠某些信号对吧。而且肯定不是轮询对吧。那么这个信号…它不就是个异步开始信号么。不管这个Call干了啥,它的作用一定是通知调度器我开始等了,那它就是个异步开始信号。
知道这个问题的答案就是要想明白,任何性质的多线程系统的非阻塞实现一定要涉及这个异步开始的状态,不管它在哪个层面上产生和捕捉,系统必须要能知道等待的发生才能将处理器交给别的线程(准确说是别的Context)。
那么我们再从协程库的角度看,协程库怎么知道协程被进入等IO的状态了?库不是调度器,不会(不能假设会)得到OS的调度细节,那么我怎么获得这个异步开始信号?
那么只能通过封装或者Hook来产生或者截获这个信号了。
那么题主的问题就很明白了。当我查询数据库的时候,我能让协程库释放当前的处理器控制权吗?能也不能,取决于你的数据库查询机制的实现,需要不需要你的线程活在那儿接数据。当然,只要你知道协程库的实现方法,当然可以告诉协程库释放你的协程。至于这么做会有多heavy,主要还是取决于你查询数据库的方法的实现。
是的,在协程中调用阻塞式i/o会阻塞整个线程,因为协程之间的调度是用户在协程中主动调用协程切换功能(比如yield语句)手动实现的,而不是系统自动强制执行的,如果一个协程调用了阻塞式i/o,这个协程就被阻塞了,哪里还有机会调用协程切换功能呢?既然无法调用协程切换功能去执行其它协程,也就意味着整个线程被阻塞了,所以协程中是不能直接调用任何会阻塞线程的功能的,需要进行封装。
对阻塞式操作的封装不是简单的让出协程执行权,因为一旦让出执行权,该协程就不会被继续执行,后续的阻塞式操作也就没有机会被完成了,正确的做法是新建一个线程或者从线程池中分配一个线程,在这个线程中执行需要的阻塞式操作,同时将当前协程休眠(让出执行权),在新线程中的操作完成后,再唤醒协程。
是的,需要自己完成,但是并不繁碎,对现代C++来说,也就几行代码的事情。Go语言的”协程(goroutine)”与通常的协程(co-routine)有些差别,因为Go语言的运行时具有协程调度功能,可以在一个协程阻塞线程时(每个协程每次只允许运行10ms,超过该时间则认定为线程被阻塞),将原本需要由该线程执行的协程调度到其它线程执行,未封装的阻塞接口问题也因此得到了解决——可以理解为,耗时超过10ms的操作会被视为是阻塞操作,会被Go语言运行时自动转移到单独的线程执行。
https://my.oschina.net/tjt/blog/906200
阻塞还是非阻塞,关注的是接口调用(发出请求)后等待数据返回时的状态。被挂起无法执行其他操作的则是阻塞型的,可以被立即「抽离」去完成其他「任务」的则是非阻塞型的。
挂起就不能干事情了吗,一个线程读文件,被阻塞了,资源不是会出让.coroutine也是把,但是比如go的routine,go的调度器会把处于阻塞的的go程上的资源分配给其他go程
异步和同步关注的是任务完成时消息通知的方式。系统内核获取到的数据到底如何返回给应用层呢?这里不同类型的操作便体现的是同步和异步的区别.。同步:应用层自己去想内核询问(轮询?).异步:内核主动通知应用层数据