我很习惯在Haskell中编程时依赖GHC的forkIO
来实现可移植的轻量级线程。
C语言中有哪些同等的库可以提供同样的可伸缩性和易用性?
我特别需要至少下列两个函数的c -等价。
forkIO :: IO () -> IO ThreadId
killThread :: ThreadId -> IO ()
我假设对于我的应用程序,如果线程只切换阻塞操作而不是强制挂起就足够了,因为所有线程都频繁阻塞网络IO,并且我只使用splice
系统调用来要求Linux内核在套接字之间推送数据。
文中有图表比较
- GNU甲状旁腺素
- Protothreads
- PM2包裹
的结果有利于原线程。由于我没有使用过任何库,而且可能还有其他库,我希望听到任何使用或开发过此类库的人的消息。
libMill可能是您正在搜索的:http://libmill.org/
它实现了Go-Lang通道风格的用户级线程。
它是由超级聪明的Martin Sústrik, ZeroMQ的创造者http://250bpm.com/开发的。所以一定很好☺
我不再有以下代码的注释,也没有任何示例-这是一个可移植的(伪)线程库,实现为预处理器宏
typedef struct
{
unsigned int magic;
unsigned short ctx;
unsigned char is_destroyed;
}
_run;
typedef struct
{
unsigned int magic;
unsigned int cnt;
}
_sem;
#define aa_RUNNER_WAITING 0
#define aa_RUNNER_YIELDED 1
#define aa_RUNNER_EXITED 2
#define aa_RUNNER_ENDED 3
#define aaRunnerCreate(rp) (rp)->magic='runr'; (rp)->ctx=0; (rp)->is_destroyed=NO
#define aaRunnerDestroy(rp) (rp)->is_destroyed=YES
#define aaRunnerThread(args) C args
#define aaRunnerBegin(rp) { C yflag=YES; if(yflag) {} switch((rp)->ctx) { case 0:
#define aaRunnerEnd(rp) } yflag=NO; if(yflag) {} aaRunnerCreate(rp); return aa_RUNNER_ENDED; }
#define aaRunnerWaitUntil(rp,condx) do { (rp)->ctx=__LINE__; case __LINE__: if(!(condx)) { return aa_RUNNER_WAITING; } } while(0)
#define aaRunnerWaitWhile(rp,condi) aaRunnerWaitUntil((rp),!(condi))
#define aaRunnerWaitThread(rp,thr) aaRunnerWaitWhile((rp),aaRunnerSchedule(thr))
#define aaRunnerWaitSpawn(rp,chl,thr) do { aaRunnerCreate((chl)); aaRunnerWaitThread((rp),(thr)); } while(0)
#define aaRunnerRestart(rp) do { aaRunnerCreate(rp); return aa_RUNNER_WAITING; } while(0)
#define aaRunnerExit(rp) do { aaRunnerCreate(rp); (rp)->magic=0; return aa_RUNNER_EXITED; } while(0)
#define aaRunnerSchedule(f) ((f)<aa_RUNNER_EXITED)
#define aaRunnerYield(rp) do { yflag=NO; (rp)->ctx=__LINE__; case __LINE__: if(!yflag||!((rp)->is_destroyed)) { return aa_RUNNER_YIELDED; } } while(0)
#define aaRunnerYieldUntil(rp,condi) do { yflag=NO; (rp)->ctx=__LINE__; case __LINE__: if(!yflag||!(condi)) { return aa_RUNNER_YIELDED; } } while(0)
#define aaRunnerSemInit(sp,c) (sp)->magic='runs'; (sp)->cnt=c
#define aaRunnerSemWait(rp,sp) do { aaRunnerWaitUntil(rp,(sp)->cnt>0); --(sp)->cnt; } while(0)
#define aaRunnerSemSignal(rp,sp) ++(sp)->cnt
使用POSIX线程。它们在任何现代实现中都是"绿色的",不是在"绿色线程"的意义上,而是在轻量级和高效的意义上。没有一种可移植的方法可以在纯C或POSIX减线程之上滚动自己的线程。正如OP所提到的,有一些库以不可移植的方式实现绿色线程/协同例程(通常尽管声称可移植性)。
最接近可移植的方法是使用makecontext
/swapcontext
,不幸的是,这不能很好地执行,因为它必须在"线程"之间的每个开关上进行系统调用来保存/恢复信号掩码。这使得在"绿色"线程之间的切换比在"真正的"POSIX线程实现上的内核级线程之间的上下文切换更昂贵,并且基本上否定了所谓的"绿色线程"的任何好处。
不关心信号掩码的不可移植方法可以使用特定于机器的asm完全在用户空间中进行上下文切换,理论上比内核级线程性能更好,但是一旦引入IO,性能就会再次飞出窗口,因为即将执行IO的线程必须首先进行昂贵的测试来检查操作是否会阻塞,如果是,将控制权移交给另一个线程。
我坚持我的立场,"绿线"是一个早已过时的想法。这似乎也是Austin Group(负责POSIX)的立场,他们在POSIX 2008中删除了ucontext
函数,并建议用POSIX线程替换(这现在是一个强制功能)。