来自plan9的libthread
2014-10-12
介绍
plan9是一个很先进的操作系统,没能流行实在太可惜了。Go算是继承了plan9衣钵并广为人知的。像acme就没这么好运了,这么好的东西居然没人发现。今天我又从plan9port中扒了一个好东西出来,那就是libthread。
libthread是什么呢?它是一个协程的库,简单地说就是这个库让你能够在C中使用goroutine和channel。其实早在Go之前,这东西就存在了,应该说是Go语言它爹了。好东西总是要发光的,就算以前没发光,Go语言同样技术换个名字再放出去,就发光了。我重新整理了一下代码,开了个项目,名称是grt,意思go runtime,学着crt取的名字。
下载地址:https://github.com/tiancaiamao/grt
使用场景
考虑可能的使用场景,比如项目不想转到Go,或者还是想手动管理内存,又想使用goroutine那样的基础设施,那么grt就值得考虑了。
再比如说,有些遗留代码,用Go重写工作量很多,而用cgo又担心性能问题,那么迁移到grt就很合适。
API
这个库实现了procs,threads,channels和locks。
int proccreate(void (f)(void arg), void *arg, unsigned int stacksize);
创建一个proc,proc是一个操作系统线程。
int threadcreate(void (f)(void arg), void *arg, unsigned int stacksize);
创建一个thread,这里的thread不是操作系统线程而是类似goroutine的东西。所有的thread会在procs之运行。
Channel* chancreate(int elemsize, int elemcount);
chancreate创建一个带缓存或者不带缓存的channel。element无类型,但是每个element大小必须相同。
int send(Channel c, void v); int recv(Channel c, void v);
send将指针v指向的内容写到channel c中。send会阻塞直到channel中有空间,或者有其它thread向channel中读取。类似地,recv向channel中读一个元素。如果channel暂时不可读则recv会阻塞。
typedef struct Alt { Channel c; / channel */ void v; / pointer to value */ Altop op; / operation / Channel **; ulong; } Alt; int alt(Alt alts[]);
Alt就是Go语言中的select操作。
int f(Channel c0, Channel c1) { int v0; char v1; Alt a[] = { / c v op / {c0, &v0, CHANRCV}, {c1, &v1, CHANRCV}, {nil, nil, CHANEND}, }; for (;;) switch(alt(a)) { case 0: fprint(2, "got int %d\n", v0); break; case 1: fprint(2, "got char %c\n", v1); break; default: error("impossible"); } }
原理
其实原理不复杂,我以前也写过类似的玩意。
主要涉及的就是保存上下文,封装非阻塞io,实现调度,然后channel提供coroutine之间通信。
保存上下文可以用getcontext做,不过getcontext是系统调用,会陷入内核,性能不太好。grt的保存上下文是用汇编写的。
调度跟早期版本的Go很像,proc对应于Go的结构体M,thread对应于结构体G。每个proc执行thread的函数,遇到阻塞时切换thread。
复杂一点的地方是线程和协和实现m:n调度。另外,对于signal处理也是复杂一些的。应该说,unix的signal跟posix的pthread起源本来就不是同一套,本身就是比较黑暗的。
相似项目
Go语言,熟习Go语言一眼就能理解grt了。本来libthread是Go的前身。
libtask,提供了几乎同样的接口。事实上作者是同一人,不过libtask是单线程的。而grt是多线程的。libtask的代码更简单一些,有很大的学习价值,但grt更实用一些。
skynet,也提供了类似的基础设施。不过skynet是Actor模型,而grt准确来说是CSP模型。另外,skynet在C的层面并没有提供协程机制,只是一个消息分发和调度,保存上下文是通过lua层面实现的,而grt是纯C。
结语
最后说一句,这东西不是我写的,我只是从plan9port中扒的代码。
———-2016.3.19更新———-
grt被我用来弄另一个项目名字了,内容已经完全变了。如果想看看libthread,可以自去plan9port扒一扒代码。