编程杂谈

异步编程

Posted by Liangjf on May 6, 2019

本文谈谈异步编程,在摩尔定律逐渐失效的今天,单核的性能逐渐不能满足人们的需求了,然而多核的编程应用对于我们来说是比较难的(所以go等现代语言才会火热~ ~)

为什么需要异步编程?

  • 当硬件发展到一定阶段,单核的性能很难再飞速增长
  • 多核的数量在增加,但在引用层面是比较难真正很好的应用。
  • 锁的开销很大。在传统的多进程/多线程的编程中,是保证数据安全的重要手段。由于资源(file, memory)的竞争, 进程/线程不得不阻塞等待。据实验测试,一个高并发的应用,20%~70%的时间可能耗在无谓的锁等待上。
  • 数据分配在一个核上,可能复制和使用在别的核上例如一个网卡的中断程序运行在一个core上,而后续的数据包的处理可能迁移到别的core上,这样CPU的cache line频繁的miss,造成性能的penalty
  • 用户态/内核态,进程线程/中断上下文切换的开销

同步和异步区别是什么?

  • 同步:一个操作必须等待它调用的其他操作完成后才能继续进行下一步的操作。
  • 异步:无需等待,当一个操作调用一个会阻塞的操作时,它会接着做别的事情,等那个阻塞的操作完成时,会发一个event通知它,那么它接着处理这个阻塞的操作。

尽管从操作系统角度来说,本质上系统是并发执行的:当一个process/thread 调用一个阻塞的系统调用时,OS会自动切换到另一个合适的process/thread去执行。这就是上下文切换

同步编程是有开销的:首先,fork一个新的process或者thread很慢,再加上如果大量的操作被阻塞,随之发生的频繁进程/线程上下文切换的代价也很大。除此之外,每个process/thread需要有自己独立的栈空间,如果系统中process/thread很多,栈的内存开销也会很大。

一个server是异步的,那么它本质上是事件驱动的(event-driven)。通常只有一个thread,这个thread就是一个迭代循环执行。每次迭代它都要轮询(epoll)有没有新的事件(event)要处理,如果有,可以调用相应的已经注册好的具体事件处理函数。

现在大多高并发的网络框架都是基于事件驱动的。如libevent,redis(自己实现一个简易版)等。