要搞清楚这个问题,首先来看看以下几个问题:
并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。
并行:在操作系统中,一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。
理解:
1)并发是两个任务可以在重叠的时间段内启动,运行和完成;并行是任务在同一时间运行,例如,在多核处理器上。
2)并发是独立执行过程的组合,而并行是同时执行(可能相关的)计算。
3)并发是一次处理很多事情,并行是同时做很多事情。
如下图所示,箭头表示空闲时间:
总结:并发就是指代码逻辑上可以并行,有并行的潜力,但是不一定当前是真的以物理并行的方式运行。并发指的是代码的性质,并行指的是物理运行状态。
顾名思义,并发强调的是一起出发,并行强调的是一起执行。并发的反义是顺序,并行的反义是串行。并发并行不是互斥概念,只不过并发强调任务的抽象调度,并行强调任务的实际执行。
BIO(同步阻塞):
步骤:1、用户发出请求。
2、一致等待数据是否准备好,如果数据没有准备好则进行阻塞。
3、数据准备好后,将数据从内核复制到用户空间。
4、数据返回给程序。
NIO(同步非阻塞):
非阻塞忙轮询:数据没来,进程就不停的去检测数据(轮询的方式),直到数据来。
IO多路复用:
如上图所示,和非阻塞一样,在后面也是阻塞的,但是在获取数据时不再由自己去询问操作系统,而是统一交给一个内核线程去处理。
虽然上述方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的(在select函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。如果用户线程只注册自己感兴趣的socket或者IO请求,然后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU的利用率。
IO多路复用模型使用了Reactor设计模式实现了这一机制。
Io多路复用有三种发方式select,poll,epoll
select: 注册事件由数组管理, 数组是有长度的, 32位机上限1024, 64位机上限2048. 轮询查找时需要遍历数组。
poll: 把select的数组采用链表实现, 因此没了最大数量的限制。
epoll方式: 基于事件回调机制, 回调时直接通知进程, 无须使用某种方式来查看状态。
拓展一:什么是c10k?
最初的服务器是基于进程/线程模型。新到来一个TCP连接,就需要分配一个进程。假如有C10K,就需要创建1W个进程,可想而知单机是无法承受的。那么如何突破单机性能是高性能网络编程必须要面对的问题,进而这些局限和问题就统称为C10K问题。
拓展二:同步、异步、阻塞、非阻塞概念?
解决方案:每个进程/线程同时处理多个连接(IO多路复用)
单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),这样避免了不必要的上下文切换和竞争条件(锁)。
redis采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求。
Redis的I/O多路复用:
Redis 服务采用 Reactor 的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符)
文件事件处理器使用 I/O 多路复用模块同时监听多个 FD,当 accept、read、write 和 close 文件事件产生时,文件事件处理器就会回调 FD 绑定的事件处理器
虽然整个文件事件处理器是在单线程上运行的,但是通过 I/O 多路复用模块的引入,实现了同时对多个 FD 读写的监控,提高了网络通信模型的性能,同时也可以保证整个 Redis 服务实现的简单
redis的多路复用, 提供了select, epoll, evport, kqueue几种选择,在编译的时候来选择一种。
select是POSIX提供的, 一般的操作系统都有支撑;
epoll 是LINUX系统内核提供支持的;
evport是Solaris系统内核提供支持的;
kqueue是Mac 系统提供支持的。