比较几个 C 语言协程实现
Posted 车斗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了比较几个 C 语言协程实现相关的知识,希望对你有一定的参考价值。
比较几个 C 语言协程实现
coroutine.h
作者:
https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
这个是用宏方式实现的。用于对资源紧张的客户端。
protothreads
另外一个可选的协程实现是 protothreads (pt):
http://dunkels.com/adam/download/pt-1.4.tar.gz
这些用在客户端都合适,极轻量级,跨windows/linux/arm平台。
libdill & libmill
这个适合编写服务端协程程序。
http://libdill.org/
在 Linux 编译非常简单:
$ wget http://libdill.org/libdill-2.14.tar.gz
$ tar -xzf libdill-2.14.tar.gz
$ cd libdill-2.14
$ ./configure
$ make
$ sudo make install
在 Windows 上用 mingw64,我按下面的方式编译成功(未做使用测试):
$ wget http://libdill.org/libdill-2.14.tar.gz
$ tar -xzf libdill-2.14.tar.gz
$ cd libdill-2.14
$ ./configure
-
DEBUG:
$ ./libtool --tag=CC --mode=link x86_64-w64-mingw32-gcc -std=gnu99 -fvisibility=hidden -DDILL_EXPORTS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -g -O2 -no-undefined -version-info 3:0:0 -o libdill.la -rpath /mingw64/lib -lWs2_32
-
RELEASE:
$ ./libtool --tag=CC --mode=link x86_64-w64-mingw32-gcc -std=gnu99 -fvisibility=hidden -DDILL_EXPORTS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -O3 -no-undefined -version-info 3:0:0 -o libdill.la -rpath /mingw64/lib -lWs2_32
生成结果在 .libs/ 目录里:
libdill.a libdill.dll.a libdill.la libdill.lai libdill-3.dll
建立在它之上有一个有名的类似 go 的协程库: libmill,它可用像写go 一样写C协程程序。
http://libmill.org/
libdill 功能非常强大,编译时根据可以需要裁剪。
state-threads
网络服务器使用以上协程库,都力不从心,推荐 state-threads,这个是用协程库实现的网络服务器,高可用,高性能,可用根据需要在上面定制:
http://state-threads.sourceforge.net/
Windows 版本64位不好用(32位好像可用),Linux 版本测试可用。
coroutine.h 的源码
如果仅仅写个简单的协程测试程序,建议使用 coroutine.h 或者 protothreads。下面是 coroutine.h 的源码,亲测可用。VS 编译时会报错误:
error C2051: case expression not constant
解决方法就是编译器配置的问题,只需要在项目属性配置页:C++/常规general/调式格式信息debug info format 中设置为:程序数据库/Zi (切记不使用:用于编辑并继续的…/ZI)
/* coroutine.h
*
* Coroutine mechanics, implemented on top of standard ANSI C. See
* https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for
* a full discussion of the theory behind this.
*
* To use these macros to define a coroutine, you need to write a
* function that looks something like this.
*
* [Simple version using static variables (scr macros)]
* int ascending (void) {
* static int i;
*
* scrBegin;
* for (i=0; i<10; i++) {
* scrReturn(i);
* }
* scrFinish(-1);
* }
*
* [Re-entrant version using an explicit context structure (ccr macros)]
* int ascending (ccrContParam) {
* ccrBeginContext;
* int i;
* ccrEndContext(foo);
*
* ccrBegin(foo);
* for (foo->i=0; foo->i<10; foo->i++) {
* ccrReturn(foo->i);
* }
* ccrFinish(-1);
* }
*
* In the static version, you need only surround the function body
* with `scrBegin' and `scrFinish', and then you can do `scrReturn'
* within the function and on the next call control will resume
* just after the scrReturn statement. Any local variables you need
* to be persistent across an `scrReturn' must be declared static.
*
* In the re-entrant version, you need to declare your persistent
* variables between `ccrBeginContext' and `ccrEndContext'. These
* will be members of a structure whose name you specify in the
* parameter to `ccrEndContext'.
*
* The re-entrant macros will malloc() the state structure on first
* call, and free() it when `ccrFinish' is reached. If you want to
* abort in the middle, you can use `ccrStop' to free the state
* structure immediately (equivalent to an explicit return() in a
* caller-type routine).
*
* A coroutine returning void type may call `ccrReturnV',
* `ccrFinishV' and `ccrStopV', or `scrReturnV', to avoid having to
* specify an empty parameter to the ordinary return macros.
*
* Ground rules:
* - never put `ccrReturn' or `scrReturn' within an explicit `switch'.
* - never put two `ccrReturn' or `scrReturn' statements on the same
* source line.
*
* The caller of a static coroutine calls it just as if it were an
* ordinary function:
*
* void main(void) {
* int i;
* do {
* i = ascending();
* printf("got number %d\\n", i);
* } while (i != -1);
* }
*
* The caller of a re-entrant coroutine must provide a context
* variable:
*
* void main(void) {
* ccrContext z = 0;
* do {
* printf("got number %d\\n", ascending (&z));
* } while (z);
* }
*
* Note that the context variable is set back to zero when the
* coroutine terminates (by crStop, or by control reaching
* crFinish). This can make the re-entrant coroutines more useful
* than the static ones, because you can tell when they have
* finished.
*
* If you need to dispose of a crContext when it is non-zero (that
* is, if you want to stop calling a coroutine without suffering a
* memory leak), the caller should call `ccrAbort(ctx)' where `ctx'
* is the context variable.
*
* This mechanism could have been better implemented using GNU C
* and its ability to store pointers to labels, but sadly this is
* not part of the ANSI C standard and so the mechanism is done by
* case statements instead. That's why you can't put a crReturn()
* inside a switch() statement.
*/
/*
* coroutine.h is copyright 1995,2000 Simon Tatham.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* $Id$
*/
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdlib.h>
/**
* MSVC: error C2051: case expression not constant __LINE__
*
* Project -> C/C++/General -> Debug info format = /Zi (NOT ZI)
*/
/*
* `scr' macros for static coroutines.
*/
#define scrBegin static int scrLine = 0; switch(scrLine) { case 0:;
#define scrFinish(z) } return (z)
#define scrFinishV } return
#define scrReturn(z) \\
do {\\
scrLine=__LINE__;\\
return (z); case __LINE__:;\\
} while (0)
#define scrReturnV \\
do {\\
scrLine=__LINE__;\\
return; case __LINE__:;\\
} while (0)
/*
* `ccr' macros for re-entrant coroutines.
*/
#define ccrContParam void **ccrParam
#define ccrBeginContext struct ccrContextTag { int ccrLine
#define ccrEndContext(x) } *x = (struct ccrContextTag *)*ccrParam
#define ccrBegin(x) if(!x) {x= *ccrParam=malloc(sizeof(*x)); x->ccrLine=0;}\\
if (x) switch(x->ccrLine) { case 0:;
#define ccrFinish(z) } free(*ccrParam); *ccrParam=0; return (z)
#define ccrFinishV } free(*ccrParam); *ccrParam=0; return
#define ccrReturn(z) \\
do {\\
((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\\
return (z); case __LINE__:;\\
} while (0)
#define ccrReturnV \\
do {\\
((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\\
return; case __LINE__:;\\
} while (0)
#define ccrStop(z) do{ free(*ccrParam); *ccrParam=0; return (z); }while(0)
#define ccrStopV do{ free(*ccrParam); *ccrParam=0; return; }while(0)
#define ccrContext void *
#define ccrAbort(ctx) do { free (ctx); ctx = 0; } while (0)
#endif /* COROUTINE_H */
以上是关于比较几个 C 语言协程实现的主要内容,如果未能解决你的问题,请参考以下文章