协程实现
C
1. 利用 C 语言的 setjmp 和 longjmp,函数中使用 static local 的变量来保存协程内部的数据。
函数原型:
| 12
 
 | int setjmp(jmp_buf envbuf);void longjmp(jmp_buf envbuf, int val);
 
 | 
先调用setjmp,用变量envbuf记录当前的位置,然后调用longjmp,返回envbuf所记录的位置,并使setjmp的返回值为val。使用longjmp后,envbuf的内容会被销毁。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 |  1 #include <stdio.h> 2 #include <setjmp.h>
 3
 4 jmp_buf buf;
 5
 6 banana()
 7 {
 8     printf("in banana() \n");
 9     longjmp(buf,1);
 10     printf("you'll never see this,because i longjmp'd");
 11 }
 12
 13 main() {
 15     if(setjmp(buf))
 16         printf("back in main\n");
 17     else
 18     {
 19         printf("first time through\n");
 20         banana();
 }
 }
 
 | 
打印结果:
| 12
 3
 4
 5
 
 | first time through
 in banana()
 
 back in main
 
 | 
2、利用C语言语法switch-case的技巧来实现
设置一个标识符,改变标识符的值,通过switch-case对标识符值的判断操纵各协程函数轮流执行。
每个协程函数可配一个结构体,保存栈内容和状态机。
代码:https://github.com/georgeredinger/protothreads
3、使用汇编代码来切换上下文(实现c协程) 。
构建一个结构体保存栈内容和当前位置等上下文信息,利用汇编语言的跳转实现协程功能。
详情见:https://www.cnblogs.com/heluan/p/9899824.html
4、利用操作系统提供的接口:Linux的ucontext,Windows的Fiber。(云风的coroutine)
ucontext: makecontext()       创建上下文     
      getcontext()         读取上下文
      setcontext()         设置上下文
      swapcontext()        跳转上下文
Fiber(纤程):ConverThreadToFiber()    从当前线程进入纤程
      CreateFiber()         创建新纤程
      SwitchToFiber()        切换到纤程
      DeleteFiber()         删除纤程
                    如果删除当前纤程,会导致它所在的线程退出 
操作系统的接口函数本身,提供了保存栈内容的功能。 
C++
说到 c++ 上的协程,boost 里其实已经有相关的实现了,不过接口上看用起来有些麻烦,单纯从语法上来说,我觉得 Lua 的协程最简洁易用了,概念上也比较直接,为什么不做一个类似的呢?所以我就打算照着 Lua 来山寨一个,只需要支持四个接口就够了:
| 12
 3
 4
 
 | create coroutinerun/resume coroutine
 Yield running corouinte
 IsCoroutineAlive
 
 | 
存与恢复上下文
实现协程/线程,最麻烦莫过于保存和切换上下文了,好在 makecontext,swapcontext 这几个函数相当好用,已经完全帮忙解决了这个难题:makecontext 可以帮我们建立起协程的上下文,swapcontext 则可以切换不同的上下文,从而实现那种把当前函数暂时停住,切换出去执行别的函数然后再切换回来继续执行的效果:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 
 | #include <iostream>#include <ucontext.h>
 using namespace std;
 
 static char g_stack[2048];
 static ucontext_t ctx,ctx_main;
 
 void func()
 {
 
 cout << "enter func" << endl;
 
 swapcontext(&ctx, &ctx_main);
 
 cout << "func1 resume from yield" << endl;
 
 }
 
 int main()
 {
 getcontext(&ctx);
 ctx.uc_stack.ss_sp = g_stack;
 ctx.uc_stack.ss_size = sizeof g_stack;
 ctx.uc_link = &ctx_main;
 
 makecontext(&ctx, func, 0);
 
 cout << "in main, before coroutine starts" << endl;
 
 swapcontext(&ctx_main, &ctx);
 
 cout << "back to main" << endl;
 
 swapcontext(&ctx_main, &ctx);
 
 cout << "back to main again" << endl;
 return 0;
 }
 
 | 
如上代码所示,显然我们只要简单包装一下 swapcontext,很容易就可以实现 Yield 和 Resume,有了它们的帮助协程做起来就容易多了。
使用与实现
在使用 makecontext,swapcontext 的基础上,参看这里,代码写下来总共才200多行,出乎意料的简单,用起来也很方便了:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 
 | #include "coroutine.h"
 #include <iostream>
 
 using namespace std;
 
 CoroutineScheduler* sched = NULL;
 
 void func1(void* arg)
 {
 uintptr_t ret;
 cout << "function1 a now!,arg:" << arg << ", start to yield." << endl;
 ret = sched->Yield((uintptr_t)"func1 yield 1");
 cout << "1.fun1 return from yield:" << (const char*)ret << endl;
 ret = sched->Yield((uintptr_t)"func1 yield 2");
 cout << "2.fun1 return from yield:" << (const char*)ret << ", going to stop" << endl;
 
 }
 
 void func2(void* s)
 {
 cout << "function2 a now!, arg:" << s << ", start to yield." << endl;
 const char* y = (const char*)sched->Yield((uintptr_t)"func2 yield 1");
 cout << "fun2 return from yield:" << y <<", going to stop" << endl;
 }
 
 int main()
 {
 sched = new CoroutineScheduler();
 
 bool stop = false;
 int f1 = sched->CreateCoroutine(func1, (void*)111);
 int f2 = sched->CreateCoroutine(func2, (void*)222);
 
 while (!stop)
 {
 stop = true;
 if (sched->IsCoroutineAlive(f1))
 {
 stop = false;
 const char* y1 = (const char*)sched->ResumeCoroutine(f1, (uintptr_t)"resume func1");
 cout << "func1 yield:" << y1 << endl;
 }
 
 if (sched->IsCoroutineAlive(f2))
 {
 stop = false;
 const char* y2 = (const char*)sched->ResumeCoroutine(f2, (uintptr_t)"resume func2");
 cout << "func2 yield:" << y2 << endl;
 }
 }
 
 delete sched;
 return 0;
 }
 
 | 
如上所示,Yield 里传的参数会在调用 Resume 时被返回,同理 Resume 里的第二个参数,会在 Yield 里被返回,这种机制也是模仿 Lua 来的,有些时候可以用来在协程间传递一些参数,很方便,看起来也挺酷的,但在实现上却相当地简洁,核心代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 
 | void CoroutineScheduler::SchedulerImpl::Schedule(void* arg)
 {
 assert(arg);
 SchedulerImpl* sched = (SchedulerImpl*) arg;
 
 int running = sched->running_;
 
 coroutine* cor = sched->id2routine_[running];
 assert(cor);
 
 cor->func(cor->arg);
 
 sched->running_ = -1;
 cor->status = CO_FINISHED;
 }
 
 
 uintptr_t CoroutineScheduler::SchedulerImpl::ResumeCoroutine(int id, uintptr_t y)
 {
 coroutine* cor = id2routine_[id];
 if (cor == NULL || cor->status == CO_RUNNING) return 0;
 
 cor->yield = y;
 switch (cor->status)
 {
 case CO_READY:
 {
 getcontext(&cor->cxt);
 
 cor->status = CO_RUNNING;
 cor->cxt.uc_stack.ss_sp = cor->stack;
 cor->cxt.uc_stack.ss_size = stacksize_;
 
 cor->cxt.uc_link = &mainContext_;
 
 running_ = id;
 makecontext(&cor->cxt, (void (*)())Schedule, 1, this);
 swapcontext(&mainContext_, &cor->cxt);
 }
 break;
 case CO_SUSPENDED:
 {
 running_ = id;
 cor->status = CO_RUNNING;
 swapcontext(&mainContext_, &cor->cxt);
 }
 break;
 default:
 assert(0);
 }
 
 uintptr_t ret = cor->yield;
 
 if (running_ == -1 && cor->status == CO_FINISHED) DestroyCoroutine(id);
 
 return ret;
 }
 
 uintptr_t CoroutineScheduler::SchedulerImpl::Yield(uintptr_t y)
 {
 if (running_ < 0) return 0;
 
 int cur = running_;
 running_ = -1;
 
 coroutine* cor = id2routine_[cur];
 
 cor->yield = y;
 cor->status = CO_SUSPENDED;
 
 swapcontext(&cor->cxt, &mainContext_);
 return cor->yield;
 }
 
 | 
ref:  https://www.cnblogs.com/catch/p/3617962.html