服务器两种高效的并发模式

一、并发编程与并发模式

并发编程主要是为了让程序同时执行多个任务,并发编程对计算精密型没有优势,反而由于任务的切换使得效率变低。如果程序是IO精密型的,则由于IO操作远没有CPU的计算速度快,所以让程序阻塞于IO操作将浪费大量的CPU时间。如果程序有多个线程,则当前被IO操作阻塞的线程可主动放弃CPU,将执行权转给其它线程。

IO精密型和cpu精密型可以参考此文:CPU-bound(计算密集型) 和I/O bound(I/O密集型)

并发编程主要有多线程和多进程,这里我们先讨论并发模式,并发模式指:IO处理单元和多个逻辑直接协调完成任务的方法。服务器主要有两种并发编程模式:

  • 半同步/半异步模式(half-sync/half-async)
  • 领导者/追随者模式(Leader/Followers)

二、半同步/半异步模式(half-sync/half-async

这里的“同步”和“异步”和“IO”的“同步”“异步”是完全不同的概念。在IO模型中,“同步”和“异步”区分的是内核向应用程序通知的是何种IO事件(是就绪事件还是完成事件),以及该由谁来完成IO读写(是应用程序还是内核)。在并发模式中,“同步”指的是程序完全按照代码序列的顺序执行;“异步”指的是程序的执行需要由系统事件来驱动。常见的系统事件包括中断、信号等。

下图1描述了并发模式同步读操作(图1a)和异步读操作(图1b)

图1并发模式同步读(a)和异步读(b)

已同步方式运行的线程为同步线程,异步方式运行的为异步线性,异步线程的执行效率高,实时性强,但编写异步方式执行的程序相对复杂,难于调试和扩展,而且不适合于大量的并发。同步线程则相反,它虽然效率相对较低,实时性较差,但逻辑简单。

因此对应服务器要求实时性及同时处理多个请求的程序,可以同时使用同步线程和异步线程即采用半同步/半异步模式。同步线程用于处理客户逻辑,异步线程用于处理IO事件。异步线程监听到客户请求后,就将其封装成请求对象并插入到请求队列中。请求队列将通知某个工作在同步模式的工作线程来读取并处理该请求对象。具体哪个线性处理取决于请求队列的设计。下图2为半同步/半异步的工作流程


图2半同步/半异步的工作流程

在半同步/半异步模式可以变体成为半同步/半反应堆(half-sync/half-reactive),如下图3

图3半同步/半反应堆模式

半同步/半反应堆中,异步线程只有一个,即主线程,他负责监听所有事件,有事件发生则将事件插入请求队列中。工作线程休眠在请求队列中,当任务到来时,通过竞争获取任务处理权。

在上图3半同步/半反应堆中,主线程插入工作队列的为就绪的连接socket,他要求工作线程自己socket读取数据和往socket写入服务器应答,所有可以看作Reactor模式。实际也可以模拟为Proactor模式,即主线程完成数据的读写,将数据封装成任务对象插入请求队列,工作线程从请求队列取出任务对象处理。(Reactor模式和Reactor模式可以参考此文:服务器两种高效的事件处理模式

半同步半反应堆模式存在如下缺点:

  1. 主线程和工作线程共享请求队列,对请求队列的操作需求加锁,耗费CPU时间。
  2. 每一个工作线程在同一时间只能处理一个客户请求。客户数量多,工作线程少,请求队列任务堆积,响应满,如果添加试图通过增加线程则,由于线程切换导致的CPU时间消耗。
    这里我们再介绍一种高效的半同步/半异步模式:每个工作线程都能同时处理多个客户连接。
图4 高效半同步/半异步模式

主线程只管理监听socket,连接socket由工作线程来管理。当有新的连接到来时,主线程就接受之并将新返回的连接socket派发给某个工作线程,此后该socket上的任何IO操作都由被选中的工作线程来处理,直到客户端关闭连接。主线程向工作线程派发socket的最简单的方式,是往它和工作线程之间的管道里写数据。工作线程检测到管道里有数据可读时,就分析是否是一个新的客户连接请求到来。如果是,则把该新socket上的读写事件注册到自己的epoll内核事件表中。每个线程(主线程和工作线程)都维持自己的事件循环,它们各自独立的监听不同的事件。因此在这种模式中,每个线程都工作在异步模式,所以它并非严格意义上的半同步半异步模式。

三、领导者/追随者模式(Leader/Followers

领导者/追随者模式是多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件的一种模式。在任意时间点,程序都仅有一个领导者线程,它负责监听IO事件。而其他线程都是追随者,它们休眠在线程池中等待成为新的领导者。当前的领导者如果检测到IO事件,首先要从线程池中推选出新的领导者线程,然后处理IO事件。此时,新的领导者等待新的IO事件,而原来的领导者则处理IO事件,二者实现了并发。包含如下几个组件:

  • 句柄集(HandleSet)
  • 线程集(ThreadSet)
  • 事件处理器(EventHandler)
  • 具体的事件处理器(ConcreteEventHandler)。

关系如下图5


图5 领导者/追随者模式的组件

1、句柄集
句柄表示IO资源,linux下通常是文件描述符。句柄集使用wait_for_event方法监听这些句柄上的IO事件,并将其中的就绪事件通知给领导者线程。领导者调用绑定到Handle上的事件处理器来处理事件。绑定是通过句柄集的register_handle方法实现的。

2、线程集
所有工作线程的管理者,负责线程同步、推选新领导。线程在任一时间必处于以下三种状态之一:

  • Leader:领导者线程,负责等待句柄集上的IO事件。
  • Processing:线程正在处理事件。领导者检测到IO事件后可以转移至Processing状态处理该事件,并调用promote_new_leader方法推选新领导者;也可以指定其他追随者来处理事件,此时领导者地位不变。当处于Processing状态的线程处理完事件后,如果当前线程集中没有领导者,则它将成为新领导者,否则它直接转为追随者。
  • Follower:线程处于追随者身份,通过调用线程集的join方法等待成为新领导者,也可能被领导者指定来处理新的事件。

这三种状态之间的转换关系图如下图6:

图6 领导者/追随者模式的状态转移

(注意,领导者推选新领导和追随者等待成为新领导这两个操作都会修改线程集,因此线程集提供一个Synchronizer来同步。)

3、事件处理器和具体的事件处理器
事件处理器通常包含一个或多个回调函数handle_event。这些回调函数用于处理事件对应的业务逻辑。事件处理器在使用前需要被绑定到某个句柄上,当该句柄有事件发生时,领导者就执行绑定的事件处理器的回调函数。具体的事件处理器是事件处理器的派生类。它们重新实现基类的handle_event方法,以处理特定的任务。

由于领导者自己监听IO事件并处理客户请求,该模式不需要在线程间传递额外数据,也无需像半同步/半反应堆模式那样在线程间同步对请求队列的访问。但是,该模式的明显缺点是仅支持一个事件源集合,因此也无法让每个工作线程独立管理多个客户连接。
我们将领导者/追随者模式的工作流程总结如下图7

图7 领导者/追随者模式的工作流程

注(本文内容参考 Linux高性能服务器编程——第八章 游双著)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,376评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • 接上一篇《那些山,那些水【Part I】》,依然是六月份西藏旅拍的照片,每周发布9张,大部分后期是由@山里囤 完成...
    不许瞎搞阅读 470评论 0 0
  • 要如何才能将记忆里那些过去生活的片段拿出来,凑成生活本来的样子?或许永远都不可能!我们一路走着,今天比明天聪明一点...
    走鱼阅读 220评论 0 3
  • 贾宝玉林黛玉薛宝钗名字的同与异 在《红楼梦》中,写了许许多多的人物,写了大大小小的事情,描写了四大家族由盛转哀的...
    舟⼀阅读 1,125评论 7 13