#include <stdio.h> #include <unistd.h> #define THREAD_INTERVAL 500 #define cr_start() \ static int __s = 0; \ switch (__s) { \ case 0: #define cr_yield \ { __s = __LINE__; \ usleep(THREAD_INTERVAL); \ return; \ case __LINE__: ; \ } #define cr_end() \ } __s = 0; static int condition = 1; static void user_thread_1() { cr_start(); for ( ; ; ) { /* do something */ printf("Run %s\n", __FUNCTION__); cr_yield; } cr_end(); } static void user_thread_2() { cr_start(); for ( ; ; ) { if (condition) { /* do something conditional */ printf("Run %s - (1)\n", __FUNCTION__); cr_yield; } /* do something */ printf("Run %s - (2)\n", __FUNCTION__); condition = !condition; cr_yield; } cr_end(); } int main() { for ( ; ; ) { user_thread_1(); user_thread_2(); } return 0; }一般的 function/method invocation 是 context switching 的行為 (注意:這裡的 context switching 與作業系統的術語不直接對應,而是強調 stack-based operation),而 coroutine 最重要的想法就是保持上一次的 context,所以常用的實做技巧就是透過 "generator" 來實現 coroutine 中對 "yield" 的認知:"yield = context saver + jump"。由上面的程式碼列表可見,我們透過 C Macro 簡化細節以吻合 coroutine 的「表徵」,同時也可看到如此模擬出 thread scheduler 的行為,換言之,這是「合作式多工」的基礎概念。
$ ./user-thread Run user_thread_1 Run user_thread_2 - (1) Run user_thread_1 Run user_thread_2 - (2) Run user_thread_1 Run user_thread_2 - (2) Run user_thread_1 Run user_thread_2 - (1) Run user_thread_1 Run user_thread_2 - (2) Run user_thread_1 Run user_thread_2 - (2) Run user_thread_1 Run user_thread_2 - (1) ...而程式碼列表中的 usleep 則確保有機會透過 Ctrl-C 或互動式操作結束此程式,避免無謂的 busy-waiting。
interesting!
我知道python中有引入yield來作generator,
不過倒是還不太看到更廣泛的用途…
一個coroutine的實作叫Protothreads -- http://www.sics.se/~adam/pt/
由 jeul 發表於 December 11, 2006 04:46 PM这个小程式跟jserv兄上面写的一样,就是main中的判断逻辑稍微麻烦点,但是去掉了for-loop:
#include
#include
#include
static jmp_buf jmpbuf_th0;
static jmp_buf jmpbuf_th1;
static void thread_0()
{
printf("%s: 0\n", __FUNCTION__);
sleep(1);
longjmp(jmpbuf_th0, 1);
}
static void thread_1()
{
printf("%s: 0\n", __FUNCTION__);
sleep(1);
longjmp(jmpbuf_th1, 1);
}
int main()
{
int rc0, rc1 = 0;
entry_thread_0:
rc0 = setjmp(jmpbuf_th0);
if (rc0 != 0)
thread_1();
entry_thread_1:
rc1 = setjmp(jmpbuf_th1);
thread_0();
return 0;
}
To wolfhe,
Thanks for your sharing and contribution.
Neat example!