线程(七): 基于事件的并发

作者: 雪山肥鱼
时间:20210430 22:14
目的:事件并发简介

# 背景
# 简介:An Event Loop
# 重要的基础API: select()
  ## select()举例 一
  ## select()举例 二
  ## 为什么更简单? 无锁
# 问题: 阻塞的系统调用
# 问题: 状态的管理
# 问题: event-based 其他困难
# 小结

背景

在现代的操作系统中,基于事件的并发越发流行。比如node.js.
Event-base concurrency 针对两层问题。

  1. 管理并发的正确性,比如死锁,活锁,等其他多线程问题
  2. 多线程问题中,程序员几乎没有权限控制调度。程序员只是简单的创建了线程,依赖OS去合理的调度。
    要实现一个在各种不同负载下,都能够良好运行的通用调度程序,是极有难度的。因此,某些时候操作系统的调度并不是最优的。
    不用多线程,去构建并发服务器,同时保证对并发的控制,且避免之前多线程面临的问题,是个问题的关键

简介:An Event loop

  1. 等待事件的发生
  2. 检查事件类型
  3. 做事情:I/O request or 调度其他事件
//even loop
while(1) {
  events = getEvents();
  for( e in events)
    processEvent(e);
}

当事件正在处理时,他是系统中唯一发生的事件。所以调度器要做的就是 下一个事件是什么。对调度的掌控是event based contorl 的一大优点。
那么如何准确的决定下一个事件是什么,特别是当考虑到 network 和 disk 的 I/O。
特别是一个event based server 如何判断一条消息属于它的消息是否已经到达。

重要的基础API: select( ) (or poll)

select() & poll 的目的:
检查是否有任何应该关注的输入I/O。如网络应用程序(web 服务器) 希望检查是否有网络数据包已经到达,以便为他们提供服务。

int select(int nfds, 
               fd_set * readfds,
               fd_set * writefds,
               fd_set * errorfds,
               struct timval * timeout);
  • nfds, 检测后续参数中每一个 fds_set 中 0 到第 nfds-1 个 fdS
  • slecect 检测 readfds(准备好读), writefd(准备好写), errorfds(准备好接受意外事件), timeout(超时)。

注意点:

  1. select 可以让你同时检查 是否有可读的文件描述符,是否有可写的文件描述符。
  • 可读: 有包进来,读包, 处理包
  • 可写: 写入数据把包发出去

2.超时设置成NULL,代表 select 可以等下去,直到某个fd准备就绪(不太理解这个准备就绪,是有数据要读可写?),当然也可以设置超时事件。
select 和 poll 的原理大致相同。
当然还有epoll 我们后续讨论
这些基于事件的api构造了 非阻塞的 事件循环。可以用来检查 从sockets 传来的数据,并在需要时做出回复。

select ( ) 举例。

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main(void) {
  //open and setup a bunch of sockets (not shown)
  //main loop  
  while(1) {
      fd_set readFDs;
      FD_ZERO(&readFDs);
      
      // now set the bits for the descriptors
      // this server is intrested in
      // for simplicity, all of threm from imn to max)
      
      int fd;
      for (fd = minFD; fd < maxFD; fd++)
          FD_SET(fd, &readFDs);
      // do the select
      int rc = select(maxFD +1, &readFDs, NULL, NULL, NULL);
      
       // check wich acturally have data using FD_INSSET()
      int fd;
      for(fd = minFD; fd < maxFD; fd++)
          if(FD_ISSET(fd, &readFDs))
              processFD(fd);
  }
}

假设开了 很多socktes, 我们用select 查看哪些网络描述符在他们上面有传入消息。

  1. 清空fd_set
  2. 设置 fd_set,我们监视从 0 到 maxFD-1 这些fd
  3. select 检测
  4. 一旦select 返回,重新过一遍fd_set 的每一个 fd。得到数据并处理。
    真正的服务器要比者复杂的多。涉及在发送消息,发出磁盘I/O和许多其他细节。 后续继续学习 UNIX 环境高级编程把。

为什么更简单? 无锁

在单cpu 和 基于事件的应用中,不存在并行问题。特别是因为 event-based 当处理一个时间时,不会被其他线程中断,event-based是单线程的(单CPU)。
提示,不要阻塞based event server.

问题:阻塞的系统调用

当事件发起的请求中,有阻塞的系统调用该怎么处理呢?
例如 客户端发向服务器发起请求,求取读服务器磁盘,并返回文件内容。就像http请求一样。
通常event handler 会发起 open() 系统调用 打开文件,紧跟着一些列的read() 去 读文件。当文件读入内存,服务器会发送结果给客户端。

open 和 read 函数向内存发送请求时,数据可能还不在内存中,那么就要访问硬盘,则会花费很多时间才能给client返回数据。

  • 对于 thread-based server 来说,这不是事儿,当线程发起I/O请求时,CPU 会切换到其他线程,可以是服务器正常运行。这种I/O 复用让多线程服务看起来 自然 直接。

  • 对于event - based 的服务器来说,一个事件只要block 了,那服务器就会处于idle态,会造成资源的浪费。所以,对于event-based 服务器来说,非阻塞的调用时必须的。

解决方案: 异步I/O

现代操作系统中会引入 Asynchronous I/O。App 发出系统调用,然后立刻返回。
存在另外的接口,让App 能够确定确定I/O 是否已经完成。

int aio_read();
int aio_error();

比如说 异步方式去读,然后立刻返回,定期利用 aio_error()检查 异步I/O是否完成。
非常明显的缺点,总是要检查 I/O(如果时100个I/O请求呢)是否已经结束,很烦。
Unix 采用的方式 是 中断,interrupt. 当I/O 结束时,UNIX 会发送信号给调用者,caller 就不用一直去问,I/O 有没有结束了。
轮询 vs 中断 在设备系统中也经常见到。尤其时后续的I/O 设备章节。

补充 : 信号

kill -l 查看所有信号


信号种类.png
#include <stdio.h>
#include <signal.h>

void main( int arg) {
  printf("stop wakin me up...\n");
}

int main(int argc, char ** argv) {
  signal(SIGHUP, handler); 
  while(1)
    ;
  return 0;
}

关于 sighup 信号的介绍,进程 进程组 会话之间的关系
https://blog.csdn.net/z_ryan/article/details/80952498

通常是 事件+多线程 hybird approach

  • 事件用于处理网络包
  • 多线程/线程池 用来管理未完成的I/O

另一个问题: 状态的管理

  • 对于多线程,所有的状态都在线程独占的 stack 中
  • 对于事件,发出异步I/O请求后,需要打包当前的程序状态,等着I/O请求完毕,再回头利用这些状态,进行后续处理。

举例多线程:

int rc = read(fd, buffer, size);
rc = write(sd, buffer,size);

read读好后,线程立刻直到去往哪一个fd(sd)里写。
对于异步的事件处理机制来说,就比较麻烦,需要定期检查 aio_error()的值,或者等着被通知 fd已经准备就绪。那么后续event-based server 收到 IO 结束的通知,该怎么做呢,应该如何找到要发送的 sd??

  1. 在某个地方记录 完成处理该事件的 所需要的信息(存接下来要用到的SD)
  2. I/O 完成时,找到所需信息(sd),处理事件

以上述代码为例。

  1. 将sd 存于某处,并且可以通过fd 查询到,比如一个 hash table
  2. 当 I/O 结束,event handler 会通过fd 查找sd。
  3. 往sd 里写东西,完成剩余工作

event-based 的其他困难

  • event-based 搬运到 多CPU 上,就会遇到很多困难。event-based单线程的 eventhandler 优势,荡然无存。在当代多核cpu的前提背景下,无所的 事件 处理机制,是永远不会存在的了。
  • 面对系统类的事件,比如 paging,在等page - fault 的时候,event 的阻塞。这类隐式的 block 难以避免,可能会造成较大的性能问题
  • 随着事件的退役,基于事件的服务器代码难以维护,一旦一个routine从同步进化成异步,那么之前所涉及的代码都要修改,也必须要服从新的特性。阻塞事件对于服务器的性能是才难的,因此程序员必须时钟注意每个事件API 不同参数下的不同应对。
  • 异步磁盘I/O 和 异步 网络I/O 还没把发进行统一和整合,这是狠难的。比如 select 管理 未完成的I/O ,这涉及 select for networking 和 select for dis I/O 的 组合

小结

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

推荐阅读更多精彩内容