服务器编程之路:进无止境(下)

(接上文)

为了找到第二个命题的解决方法,我们可以再回过头来看看本文中第一版的服务器程序。前面也说了,第一版程序的问题在于,一条线程服务一个连接,而OS切换线程的开销很大,所以造成性能上不去。但第一版程序绝对是愉快的顺序编程。如果我们想保留顺序编程,那应该怎么克服性能方面的缺陷呢?

问题被直接导向为:既然OS调度线程很吃力,那是否存在一种“用户态线程”,由程序自己调度,让OS一边玩儿去?

先抛出答案,所谓的“用户态线程”,我们一般的实现就是“协程(coroutine)”。

在教科书上,协程的定义恐怕是这样的:“协程就是协作的子例程”。啥啥?“协作”是什么意思?“子例程”又是什么鬼?这个定义真是让人云里雾里。

我觉得协程很难用一句简短的语句定义。解释协程这个概念,必须说明以下两点:

1、协程本质上是一种算法。一个函数如果能够实现中断,后续恢复时能够从断点继续照常执行,那么这个函数就可以称为协程。

2、协程与线程的本质区别在于:线程之间是竞争cpu的;而协程之间是相互协作的,互相“禅让”cpu。协程有一个关键的原语“yield”,表示主动把cpu退让出去。yield是由用户在协程中自行调用的,理论上你可以不调用,那么这个协程就独占了整条线程的cpu资源。协程相互yield,就是协程协作的本意。

很多编程语言从语言级别上就天然支持了协程,如C#、Go、Lua。但可惜的是,作为偏底层的C/C++却不在语言中支持。

当然,C/C++可以自己实现协程。Windows有协程API(称为fiber),Linux提供了ucontext.h头文件,它允许使用者保存并回复断点上下文(当前寄存器状态与栈内存),从而实现协程。如果你想了解更多的细节,可以阅读我实现的协程代码:http://github.com/xphh/coroutine

有了协程,离我们的命题“顺序编程”还是有一段距离。事情还只讲了一半。

因为协程毕竟还是在一个线程内的,所以某一个协程阻塞了,别的协程也运行不下去了。也就是说,协程还不能等价于“用户态线程”。想要把协程当做线程用,必须考虑如何把在协程中阻塞的操作变成线程中不阻塞的操作。

“把在协程中阻塞的操作变成线程中不阻塞的操作”,这句话很拗口也很矛盾,很难直接阐释,我只能讲怎么做。

在第一版服务器程序中,我们处理一个连接,接收时往往需要阻塞在recv函数上。但实际上,recv所做的处理,无非是在等IO上的数据,在等待的过程中,cpu是被浪费掉的。所以我们要做的第一件事,就是在recv阻塞之前,yield到一个epoll协程。这个epoll协程监听所有IO,当某个IO(比方刚才那个协程中的IO)有数据时,再resume回到那个IO所在的协程。

在上面我们提到了协程的resume原语。是这样的,协程有两种编程模型,一种叫“对称协程”,其中所有的协程都可以任意yield到另一个协程;另一种叫“非对称协程”,所有协程只能yield到主协程,由主协程利用resume调用某一个协程。一般情况下我们推荐非对称协程,因为对称协程在编程上是混乱的。

这样,整个程序由一个线程的N+1个协程组成。N个协程处理N条连接,一个主协程做IO复用(epoll)。在这N个协程上我们的处理的确是阻塞的,但实际上线程并没有阻塞,线程大部分时间都在主协程的epoll上而已。

也就是说,使用协程顺序编程,我们必须提前处理掉所有的阻塞调用。所有socket的接口(connect、sendto、send、recvfrom、recv)以及sleep函数,都需要自己实现为协程版本,即:阻塞前yield出去,在主协程中等待事件触发后resume回来。

至此,命题二实现!

结束了吗?还没有呢。如果你开始想用协程进行服务器编程,那以下这些事一定得知道:

1、协程只不过是有独立栈空间的线程,如果你在协程中阻塞了,其他协程也阻塞了。

2、关于上一点,我相信大家都清楚了。你肯定会说,不是可以改写阻塞函数吗?的确可以。不过你得明白,C/C++编程发展至今,更多的是一种生态体系。可惜这种生态对协程并不友好。我们几乎必用的服务器开发相关的第三方库,如mysqlclient,libcurl等,项目中用的话,是不可能直接改源码的。所以协程运用的边界,你得考虑清楚。

3、对于多核服务器,提升性能最终还是要靠多线程或多进程。如果你想在多线程中使用协程,记住不要在多个线程中调度协程。也不是说不可以,而是这种做法和协程的出发点是相悖的。

多线程 -> 事件模型 -> 协程,这就是服务器编程之路。

时间: 2024-08-08 09:10:04

服务器编程之路:进无止境(下)的相关文章

服务器编程之路:进无止境(上)

首先不好意思,盗用了福特汽车的广告语,呵呵. 今天想在这里探讨一下高性能服务器(server)编程的一些通用技术(或者说是思想).编程技术发展至今,高性能服务器编程领域仍然是C语言的菜.而C语言在服务器编程中的技术,也不断在实践中提高,正暗含我们的题目. 有基础的初学者写的第一个基于TCP的服务器程序,想必大概是这样的: while (1) { listen();                      // TCP套接字监听 fd = accept();          // 接受远端连接

linux高性能服务器编程之poll

一.概述: 和select不同的是,poll使用一个pollfd来指向所要监听的fd,事件,返回事件.(pollfd下面详细讲.) 并且poll没有最大的文件描述符数量的限制,是自己定义一个pollfd数组来实现的. 它的缺点和select差不多,即 (1)每次调用poll,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大(2)当要确定一个文件描述符的状态时,都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大 二.poll介绍篇:  int poll(struct p

ZSTU我的编程之路

浙江理工大学我的编程之路答案 语言:C/C++ 推荐编译器:Dev-C++ / CodeBlocks 严禁直接复制提交 作弊行为将会被ACM校队禁止入队 作者:JokerNoCry 1000: 送分题-A+B Problem #include <iostream> using namespace std; int main (){ int a,b; cin>>a>>b; cout<<a+b; } 零基础学C/C++ oj 题目1001--1199 1001:

转:PHP并发IO编程之路

并发IO问题一直是服务器端编程中的技术难题,从最早的同步阻塞直接Fork进程,到Worker进程池/线程池,到现在的异步IO.协程.PHP程序员因为有强大的LAMP框架,对这类底层方面的知识知之甚少,本文目的就是详细介绍PHP进行并发IO编程的各种尝试,最后再介绍Swoole的使用,深入浅出全面解析并发IO问题. 多进程/多线程同步阻塞 最早的服务器端程序都是通过多进程.多线程来解决并发IO的问题.进程模型出现的最早,从Unix系统诞生就开始有了进程的概念.最早的服务器端程序一般都是Accept

PHP并发IO编程之路

并发IO问题一直是服务器端编程中的技术难题,从最早的同步阻塞直接Fork进程,到Worker进程池/线程池,到现在的异步IO.协程.PHP程序员因为有强大的LAMP框架,对这类底层方面的知识知之甚少,本文目的就是详细介绍PHP进行并发IO编程的各种尝试,最后再介绍Swoole的使用,深入浅出全面解析并发IO问题. 多进程/多线程同步阻塞 最早的服务器端程序都是通过多进程.多线程来解决并发IO的问题.进程模型出现的最早,从Unix系统诞生就开始有了进程的概念.最早的服务器端程序一般都是Accept

linux高性能服务器编程之epoll

一.概述: epoll是多路复用的一种,但它比select和poll更加高效.具体体现在以下几个方面: (1).select能打开的文件描述符是有一定限制的,默认情况下是2048,这对应那些大型服务器来说h是不足的.但 epoll则没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat  /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大. (2).因为文件描述符是

编程之路

转自:http://blog.csdn.net/banketree/article/details/39995209 技术永无止境,编程方向现在越来越广了,你还在迷茫地选择方向么? 本篇将简单描述编程各个方面,以及编程之路会遇到的生活问题. 问题与解答 1.编程分为几个阶段? 高手箴言中把编程分为六个阶段:1.熟练地使用某种语言(java).2.精通基于某种平台的开发(Android).3.深入地了解某个平台系统的底层(Android 底层).4.直接在平台上进行比较深层次的开发(Android

编程之路:多态和绑定与非绑定方法

多态 多态是指一类事物有多种形态 动物有多种形态:人.狗.猪 import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之二:狗 def talk(self): print(

Objective-C之父Brad Cox访谈:我的编程之路

Objective-C是苹果Mac OS X 上开发的首选语言,也正因为如此促就了它的辉煌.日前,作者Dave Dribin采访了Objective-C之父Brad Cox,一起来了解下这位大神级人物,是如何看待他的编程之路的. 众所周知,Brad Cox与Tom Love一起创造了iOS/MAC平台的首选开发语言Objective-C.构建于C语言之上的Objective-C其理念来自于公认的第二个面向对象的程序设计语言和第一个真正的集成开发环境的Smalltalk.日前,原文作者Dave D