看到有人用setjmp()/longjmp() 做的多线程例子。这种方法极容易造成堆栈冲突。非常不赞成这么用。如果用多线程,linux当然用pthread线程库。windows当然用CreateThread()。
原例子这里就不展示了。感兴趣可以自己搜索“setjmp 协程”。这里做了大幅修改,把原来的“主控(即主线程)和子线程”改为“主控(即调度程序)和两个子线程”。3个线程合作运行。但仍然有堆栈冲突的危险。最好每个线程有独立的运行栈,那样就又回到OS自己的多线程上来了。
这里分享上来,只为了爱好者扩大知识面,并不代表就可以实用了。调试,评估,然后扔掉:
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <stdlib.h>
typedef int BOOL;
#define TRUE 1
#define FALSE 0
typedef struct _Context_ {
jmp_buf mainBuf;
jmp_buf coBuf;
int finish;
struct {
jmp_buf buf;
int state;
} thread[2];
} Context;
Context gCtx;
static int next_thread;
#define resume(i) \
if (gCtx.thread[i].state==0) \
{ \
longjmp(gCtx.thread[i].buf, 1); \
}
#define yield() \
if (0 == setjmp(gCtx.coBuf)) \
{ \
memcpy(gCtx.thread[thread_id].buf, gCtx.coBuf, sizeof(jmp_buf));\
longjmp(gCtx.mainBuf, 1); \
}
void coroutine_function(void *arg)
{
int n = 0;
int thread_id = *(int *) arg;
while (TRUE) {
int i;
if (++n > 10) break;
printf("\n*** coroutine: thread_id=(%d) working \n", thread_id);
for (i = 0; i < 10; ++i) {
printf(".");
}
printf("\n*** coroutine: suspend \n");
yield();
}
printf("\n*** coroutine: finish(%d)\n", n);
gCtx.finish = 1;
gCtx.thread[thread_id].state = 1;
yield();
}
typedef void (*pf) (void *);
BOOL start_coroutine(pf func, void *arg)
{
int reserve[256];
int thread_id;
extern int run;
thread_id = next_thread++;
run = thread_id;
func(&thread_id);
return TRUE;
}
void coroutine_main(void *arg)
{
int i;
int n = 0;
int thread_id;
thread_id = *(int *) arg;
if (setjmp(gCtx.thread[thread_id].buf)!=0) goto next;
start_coroutine(coroutine_function, NULL);
next:
while (TRUE) {
if (gCtx.finish) break;
printf("\n=== main() count(%d): thread_id=(%d) working \n", ++n, thread_id);
for (i = 0; i < 10; ++i) {
printf("%d%d%d%d%s", i, i, i, i, "-");
}
printf("\n=== main: suspend \n");
yield();
}
printf("thread_id main=%d\n", thread_id);
gCtx.thread[thread_id].state = 1;
yield();
}
int run;
int sched()
{
int s;
if (run == 0) s = 1; else s = 0;
if (gCtx.thread[s].state == 0) run = s;
return run;
}
int main()
{
int i;
if (setjmp(gCtx.mainBuf)!=0) goto resched;
start_coroutine(coroutine_main, NULL);
while (TRUE) {
resched:
i = sched();
printf("sched thread(%d) to run\n", i);
if (gCtx.thread[i].state == 0) {
resume(i);
} else break;
}
printf("done\n");
return 0;
}
子线程用start_coroutine()启动。下一个子线程必须用上一个子线程启动。子线程结束后不能return返回,而是把状态置为1,表示结束,然后用yield()回到调度程序。更多的子线程需要自行改进调度程序。自己看代码吧!