在脚本语言中,coroutine 不是个新鲜词汇,比如 lua 内建 coroutine,python中的greenlet,但在C程序中,并不是太常见。

windows 下有 fiber,相关函数为

ConvertThreadToFiber()  thread 转 fiber
    CreateFiber() 创建 fiber

unix 下没有对应语义,但可以通过 setcontext() 函数来实现对应的功能。

setcontext() 早期是 POSIX 规范中的一部分,但在 POSIX.1-2008 已经移除了这个函数。
    setcontext() 说白了,就是将当前线程的处理器的寄存器的值作个保存,并切换寄存器值为目标环境。

unix下比较出名的实现有:
 
GNU Pth - The GNU Portable Threads。它是GNU在不支持线程的unix系统上提供 pthread 的 API,IO函数都基于select实现的,并用 select 模拟了 poll 函数。
函数是 pth_ 前缀的,有线程相关API 和 fork,select,read,write 等函数。同时在需要的情况下(configure选项 --enable-syscall-soft)会用宏替换fork,select,read,write 等函数,并提供自己 pthread 风格的API(configure选项 --enable-pthread)。
 
libtask(FreeBSD, Linux, OS X, and Solaris)。并对一些必须的IO函数作了封装,IO函数都是基于poll实现的。但不提供替换pthread,fork,select。但提供了 printf 函数家族。其实现和提供的 api 相比 pth 更小巧。
 
 
coroutine的优点:
1) 阻塞式的代码逻辑(不需要状态机,各种回调函数),达到非阻塞式编程的效果(减少 thread 数量)。
 
 
coroutine的缺点:
1) 不能有效利用SMP;
2) 非抢占式,会导致各个 coroutine 之间不能公平调度CPU资源;
3) 调试上不方便——各大调试工具都可以方便的查看每个thread的堆栈,及函数变量,但coroutine下你看不了。
 
 
 
使用场景:
1) coroutine 用在对遗留代码的复用上。对IO函数和创建线程的函数作些修改就可以用上 coroutine,程序成功转换为 IO multiplexing
2) 线程受限的情况(在脚本语言的世界很普遍)。比如python,解释器受GIL的限制,导致解释器不能真正发挥多线程的威力,这时 coroutine 就能有效提高系统 IO 负荷。就象我们看到的webpy用上gevent后,性能提升很大,特别是在有大量空闲连接时。
3) coroutine 并不适合用来运行 CPU 密集型的task。因为 CPU 密集型task会一直占用thread,导致 IO 的 poll 得不到及时运行,其它task很难有机会被调度,这样拖慢了所有task的响应速度。如果存在这样的 task,请将它移到单独的 thread 上运行。

改造实践:

这里以 xrdp 为例,

1)pth hard 模式替换IO函数,替换 pthread (链接pth的pthread库)

禁用SSL连接的情况下,可以正常工作; 启用则 SSL_accept 总是返回-1 。抓包分析,client 已经回复 Client Key Exchange, Change Cipher Spec, Finished

2)pth hard 模式替换IO函数,不替换 pthread

调用 patch 过的 select 函数时出现段错误。原因,初始化连接的时候的 rfx_context_new() 会创建 thread pool,而 thread pool 中的线程在调用 select 的时候,会被重定向为 pth_select 函数,最后在 pth_key_getdata() 处出现断错误。

3)pth soft 模式替换IO函数,不替换 pthread

禁用SSL连接的情况下,可以正常工作; 启用则 SSL_accept 总是返回-1 。抓包分析,client 已经回复 Client Key Exchange, Change Cipher Spec, Finished

这里我们看到方案2是问题最大的,原生thread 和 hard 模式IO函数相冲突。方案1,3都是因为OpenSSL不能再coroutine中正常工作。

个人经验:1)如果程序很简单,没用到任何第三方库,那直接用方案1,即替换IO和pthread。2)如果程序使用到了第三方库,那就是用方案3,并小心的限制coroutine的代码都在主线程中运行,coroutine 中的代码用上 pth 的 IO 函数。不用替换IO了,也别用 pth 的 pthread 库,原因都是为了保证第三方库的正常运转。hard模式替换的IO函数,看其实现并没有考虑多线程的问题,所以在多 thread 环境中使用有问题;更要命是,hard模式替换函数,有些并不能正确找到原型,比如在RHEL6 x64上 waitpid sigprocmsak 等。3)如果不考虑用 pth 的模拟层,新代码更建议使用简明的 libtask。

注:

GNU Pth 编译为syscall hard模式时候,RHEL6 x64编译后运行会报找不到 sigprocmask 系统调用,增加下面两行:

intern int pth_sc_sigprocmask(int how, const sigset_t *set, sigset_t *oset)
{
    /* internal exit point for Pth */
    if (pth_syscall_fct_tab[PTH_SCF_sigprocmask].addr != NULL)
        return ((int (*)(int, const sigset_t *, sigset_t *))
               pth_syscall_fct_tab[PTH_SCF_sigprocmask].addr)
               (how, set, oset);
#if defined(HAVE_SYSCALL) && defined(SYS___sigprocmask14) /* NetBSD */
    else return (int)syscall(SYS___sigprocmask14, how, set, oset);
#elif defined(HAVE_SYSCALL) && defined(SYS_sigprocmask)
    else return (int)syscall(SYS_sigprocmask, how, set, oset);
#elif defined(HAVE_SYSCALL) && defined(SYS_rt_sigprocmask)
    else return (int)syscall(SYS_rt_sigprocmask, how, set, oset);
#else
    else PTH_SYSCALL_ERROR(-1, ENOSYS, "sigprocmask");
#endif
}

coroutine的更多相关文章

  1. Coroutine in Java - Quasar Fiber实现--转载

    转自 https://segmentfault.com/a/1190000006079389?from=groupmessage&isappinstalled=0 简介 说到协程(Corout ...

  2. The Coroutine

    关于Coroutine 说到coroutine就不的不说subroutine,也就是我们常用到的一般函数.调用一个函数开始执行,然后函数执行完成后就退出,再次调用的时候,再从头开始,调用之间是没有保存 ...

  3. c coroutine

    今天看了下云风c coroutine  代码 博客,发现 coroutine 实现原理其实还比较简单,就用户态栈切换,只需要几十行汇编,特别轻量级. 具体实现 1. 创建一个coroutine: 也就 ...

  4. lua coroutine for iterator

    背景 前面的文章演示了使用闭包函数实现 状态的迭代器. 本文演示使用 coroutine来产生迭代器的例子. coroutine迭代器例子 -- 遍历二叉树 local binary_tree = { ...

  5. python中的generator(coroutine)浅析和应用

    背景知识: 在Python中一个function要运行起来,它在python VM中需要三个东西. PyCodeObject,这个保存了函数的代码 PyFunctionObject,这个代表一个虚拟机 ...

  6. hive源码之新建一个coroutine

    最近由于项目需要读了一下云风老大的hive项目代码,因为对lua只有熟悉的水平,下面的东西必然多多错误:),只为记录. lua_State *sL = schedule_newtask(L); str ...

  7. Lua Coroutine详解

    协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈,局部变量和指令指针,同时又与其它协同程序共享全局变量和其它大部分东西.线程与协同程序的主要区别在于,一个具有多线程的程序可以同时运行几个线程 ...

  8. 【Unity3D基础教程】给初学者看的Unity教程(五):详解Unity3D中的协程(Coroutine)

    作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 为什么需要协程 在游戏中有许多过程(Proc ...

  9. Lua 协程coroutine

    协程和一般多线程的区别是,一般多线程由系统决定该哪个线程执行,是抢占式的,而协程是由每个线程自己决定自己什么时候不执行,并把执行权主动交给下一个线程. 协程是用户空间线程,操作系统其存在一无所知,所以 ...

  10. U3D中的 Coroutine程序 解析

    今天咱就说说协同程序coroutine. 什么是协同程序 先说说啥是协程:它的表现形式非常像线程,对线程有过接触的朋友可能更理解我这句话的意思,你没接触过线程,那么理解它会有一些难度.但是它不存在线程 ...

随机推荐

  1. iframe自适应高度的多种方法方法小结(转)

    对于自适应高度的代码有很多,可效率什么的考虑进来好代码就不多见了,不过思路倒是差不多的不带边框的iframe因为能和网页无缝的结合从而不刷新页面的情况下更新页面的部分数据成为可能,可是 iframe的 ...

  2. [XMPP]简易的聊天室实现[一]

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  3. bokeh-scala

    使用bokeh-scala进行数据可视化 目录 前言 bokeh简介及胡扯 bokeh-scala基本代码 我的封装 总结 一.前言 最近在使用spark集群以及geotrellis框架(相关文章见h ...

  4. MYSQL delete性能优化!

    优化项 1. low_priority 当没有连接文章表时才进行删除操作. delete low_priority from T; 优化项 2. quick 当删除行时并不删除行的索引.如果再次插入这 ...

  5. Oracle10g安装中遇到的错误及解决办法

    linux解决xhost: unable to open display实用技巧:在Linux下设置xhost方法步骤 第一步:用root登陆linux,启动vnc服务:第二步:根据vnc起来的端口, ...

  6. JavaEE Tutorials (27) - Java EE的并发工具

    27.1并发基础427 27.1.1线程和进程42827.2并发工具的主要组件42827.3并发和事务42927.4并发和安全43027.5jobs并发示例430 27.5.1运行jobs示例4302 ...

  7. UDP包的大小与MTU

    在进行UDP编程的时候,我们最容易想到的问题就是,一次发送多少bytes好?当然,这个没有唯一答案,相对于不同的系统,不同的要求,其得到的答案是不一样的,我这里仅对像ICQ一类的发送聊天消息的情况作分 ...

  8. Linux中Samba详细安装

    为了实现Windows主机与Linux服务器之间的资源共享,Linux操作系统提供了Samba服务,Samba服务为两种不同的操作系统架起了一座桥梁,使Linux系统和Windows系统之间能够实现互 ...

  9. LINUX 网络编程 原始套接字

    一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首先来说,普通的套接字无法处理ICMP.I ...

  10. Codeforces 482B Interesting Array(线段树)

    题目链接:Codeforces 482B Interesting Array 题目大意:给定一个长度为N的数组,如今有M个限制,每一个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是 ...