- 论坛徽章:
- 2
|
C下实现coroutine的库
C下实现coroutine的库 —— 不需要用户去考虑如何设置状态机, 保存context的方案—— 是有的。
coroutine的wiki页面应该就有介绍一些, 我没仔细研究。
用thread肯定可以实现, 但那代价太大, 只有理论上的价值。
下面我演示一个使用win32 fiber模拟coroutine的简陋方案。
因为coroutine的切换本来就是同步的, 所以可以避免使用thread和同步工具。
而且fiber的切换是用户态的, 只有10左右的机器指令。
coroutine的api。
- // coroutine.h
- typedef struct
- {
- void* fiber_;
- void (*f_)(void* x);
- void* x_;
- void* y_;
- void* caller_;
- } coroutine_t;
- // 这是因为win32 fiber的限制
- // 每个使用fiber的thread必须首先做一些工作
- int coroutine_init(void);
- int coroutine_create(coroutine_t* self, void (*f)(void* x), void* x);
- // C没有垃圾收集等东西, 所以需要手动释放资源
- void coroutine_destory(coroutine_t* self);
- // resume返回yield的参数
- void* coroutine_resume(coroutine_t* self);
- void coroutine_yield(void* val);
- // 同lua coroutine.running
- void* coroutine_running(void);
复制代码 那么, lua的例子就可以改写为:
- // lua_example.c
- #include <stdio.h>
- #include "coroutine.h"
- void f(void* arg)
- {
- printf("in %p step1\n", coroutine_running() );
- coroutine_yield(0);
- printf("in %p step2\n", coroutine_running() );
- }
- int main(void)
- {
- coroutine_t c1, c2;
- coroutine_init();
- coroutine_create(&c1, f, 0);
- coroutine_create(&c2, f, 0);
- printf("coroutine1 %p\n", (void*)&c1);
- printf("coroutine2 %p\n", (void*)&c2);
- coroutine_resume(&c1);
- coroutine_resume(&c2);
- coroutine_resume(&c2);
- coroutine_resume(&c1);
- coroutine_destory(&c2);
- coroutine_destory(&c1);
- return 0;
- }
复制代码 使用coroutine的rc4:
- // rc4.cpp
- #include <stdio.h>
- #include <string.h>
- #include <algorithm>
- #include "coroutine.h"
- void rc4_stream(void* arg)
- {
- unsigned char* key = (unsigned char*)arg;
- unsigned char S[256];
- for (unsigned i=0; i<256; ++i)
- S[i] = i;
- for (size_t i=0, j=0, len=strlen((char*)key); i<256; ++i)
- {
- j = (j+S[i]+key[i%len]) % 256;
- std::swap(S[i], S[j]);
- }
- for (unsigned i=0, j=0; ; )
- {
- i = (i+1) % 256;
- j = (j+S[i]) % 256;
- std::swap(S[i], S[j]);
- coroutine_yield((void*)(S[ (S[i]+S[j]) % 256 ]));
- }
- }
- int main(void)
- {
- FILE* a = fopen("a", "rb");
- FILE* b = fopen("b", "wb");
- coroutine_t g;
- coroutine_init();
- coroutine_create(&g, rc4_stream, (void*)"1212");
- for (int c; c=getc(a), c!=EOF; )
- putc((unsigned)coroutine_resume(&g) ^ c, b);
- coroutine_destory(&g);
- fclose(b);
- fclose(a);
- return 0;
- }
复制代码 请留意一下f与rc4_stream函数的代码, 可以在心里默默的"输入"试试。
是否完全不需要考虑挂起点与context的保存, 直接实现算法就可以了?
这就是coroutine的脑力优势。
当然, 这种方案肯定有缺点。
首先肯定因为使用了win32特性而不可移植。
但可以找一些跨平台的coroutine的库, 如果它们很稳定的话。
最重要的是: C语言真的需要那些花花肠子的功能吗?
我感觉并不是所有时候都需要把C武装到牙齿, 依然用简单的C实现关键的部分, 其余部分交给胶水语言去粘就完了。
因为简单是C的目标之一, 无论怎么武装, 它都是缺这缺哪的。
下面先帖一下coroutine的实现代码, 再说关于coroutine, C语言缺了哪些东西。
- // coroutine.c
- #define _WIN32_WINNT 0x0400
- #include <windows.h>
- #include "coroutine.h"
- int coroutine_init()
- {
- return ConvertThreadToFiber(0) != 0;
- }
- void* coroutine_running(void)
- {
- return GetFiberData();
- }
- static void __stdcall coroutine_start_(void* arg)
- {
- coroutine_t* self = (coroutine_t*)arg;
- self->f_(self->x_);
- SwitchToFiber(self->caller_);
- }
- int coroutine_create(coroutine_t* self, void (*f)(void* x), void* x)
- {
- self->f_ = f;
- self->x_ = x;
- self->fiber_ = CreateFiber(0, coroutine_start_, self);
- self->y_ = 0;
- return self->fiber_!=0;
- }
- void coroutine_destory(coroutine_t* self)
- {
- DeleteFiber(self->fiber_);
- }
- void* coroutine_resume(coroutine_t* self)
- {
- self->caller_ = GetCurrentFiber();
- SwitchToFiber(self->fiber_);
- return self->y_;
- }
- void coroutine_yield(void* val)
- {
- coroutine_t* self = (coroutine_t*)GetFiberData();
- self->y_ = val;
- SwitchToFiber(self->caller_);
- }
复制代码 |
|