加入收藏 | 设为首页 | 会员中心 | 我要投稿 百科站长网 (https://www.baikewang.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Unix > 正文

多进程->多线程->多路复用->非阻塞->协程?

发布时间:2022-09-16 15:31:06 所属栏目:Unix 来源:
导读:  当在读这篇文章的时候,你有没有想过,服务器是怎么把这篇文章发送给你的呢?

  说简单也简单,不就是一个用户请求吗?服务器根据请求从数据库中捞出这篇文章,然后通过网络发回去。

  说复杂也复杂
  当在读这篇文章的时候,你有没有想过,服务器是怎么把这篇文章发送给你的呢?
 
  说简单也简单,不就是一个用户请求吗?服务器根据请求从数据库中捞出这篇文章,然后通过网络发回去。
 
  说复杂也复杂,服务器是如何并行处理成千上万个用户请求呢?这里面涉及到哪些技术呢?
 
  这篇文章就来为你解答这个问题。
 
  多进程
 
  历史上最早出现也是最简单的一种并行处理多个请求的方法就是利用多进程。比如在Linux世界中,我们可以使用fork、exec等系统调用创建多个进程,我们可以在父进程中接收用户的连接请求,然后创建子进程去处理用户请求,就像这样:

  这种方法的优点就在于:编程简单,非常容易理解由于各个进程的地址空间是相互隔离的,因此一个进程崩溃后并不会影响其它进程充分利用多核资源多进程并行处理的优点很明显,但是缺点同样明显:各个进程地址空间相互隔离,这一优点也会变成缺点,那就是进程间要想通信就会变得比较困难,你需要借助进程间通信(IPC,interprocess communications)机制,想一想你现在知道哪些进程间通信机制,然后让你用代码实现呢?显然,进程间通信编程相对复杂,而且性能也是一大问题我们知道创建进程开销是比线程要大的,频繁的创建销毁进程无疑会加重系统负担。幸好,除了进程,我们还有线程。
 
  多线程
 
  不是创建进程开销大吗?不是进程间通信困难吗?这些对于线程来说统统不是问题。由于线程共享进程地址空间,因此线程间通信天然不需要借助任何通信机制,直接读取内存就好了。线程创建销毁的开销也变小了,要知道线程就像寄居蟹一样,房子(地址空间)都是进程的,自己只是一个租客,因此非常的轻量级,创建销毁的开销也非常小。
 
  我们可以为每个请求创建一个线程,即使一个线程因执行I/O操作——比如读取数据库等——被阻塞暂停运行也不会影响到其它线程,就像这样:
 
  但线程就是完美的、包治百病的吗,显然,计算机世界从来没有那么简单。由于线程共享进程地址空间,这在为线程间通信带来便利的同时也带来了无尽的麻烦。正是由于线程间共享地址空间,因此一个线程崩溃会导致整个进程崩溃退出,同时线程间通信简直太简单了,简单到线程间通信只需要直接读取内存就可以了,也简单到出现问题也极其容易,死锁、线程间的同步互斥、等等,这些极容易产生bugunix线程切换,无数程序员宝贵的时间就有相当一部分用来解决多线程带来的无尽问题。虽然线程也有缺点,但是相比多进程来说,线程更有优势,但想单纯的利用多线程就能解决高并发问题也是不切实际的。因为虽然线程创建开销相比进程小,但依然也是有开销的,对于动辄数万数十万的链接的高并发服务器来说,创建数万个线程会有性能问题,这包括内存占用、线程间切换,也就是调度的开销。因此,我们需要进一步思考。
 
  Event Loop:事件驱动
 
  到目前为止,我们提到“并行”二字就会想到进程、线程。但是,并行编程只能依赖这两项技术吗,并不是这样的。还有另一项并行技术广泛应用在GUI编程以及服务器编程中,这就是近几年非常流行的事件驱动编程,event-based concurrency。大家不要觉得这是一项很难懂的技术,实际上事件驱动编程原理上非常简单。这一技术需要两种原料:event处理event的函数,这一函数通常被称为event handler剩下的就简单了:你只需要安静的等待event到来就好,当event到来之后,检查一下event的类型,并根据该类型找到对应的event处理函数,也就是event handler,然后直接调用该event handler就好了。
 
  That's it !以上就是事件驱动编程的全部内容,是不是很简单!从上面的讨论可以看到,我们需要不断的接收event然后处理event,因此我们需要一个循环(用while或者for循环都可以),这个循环被称为Event loop。使用伪代码表示就是这样:
 
  while(true) {    event = getEvent();    handler(event);}
  Event loop中要做的事情其实是非常简单的,只需要等待event的带来,然后调用相应的event处理函数即可。注意,这段代码只需要运行在一个线程或者进程中,只需要这一个event loop就可以同时处理多个用户请求。有的同学可以依然不明白为什么这样一个event loop可以同时处理多个请求呢?原因很简单,对于web服务器来说,处理一个用户请求时大部分时间其实都用在了I/O操作上,像数据库读写、文件读写、网络读写等。当一个请求到来,简单处理之后可能就需要查询数据库等I/O操作,我们知道I/O是非常慢的,当发起I/O后我们大可以不用等待该I/O操作完成就可以继续处理接下来的用户请求。
 
  现在你应该明白了吧,虽然上一个用户请求还没有处理完我们其实就可以处理下一个用户请求了,这也是并行,这种并行就可以用事件驱动编程来处理。这就好比餐厅服务员一样,一个服务员不可能一直等上一个顾客下单、上菜、吃饭、买单之后才接待下一个顾客,服务员是怎么做的呢?当一个顾客下完单后直接处理下一个顾客,当顾客吃完饭后会自己回来买单结账的。看到了吧,同样是一个服务员也可以同时处理多个顾客,这个服务员就相当于这里的Event loop,即使这个event loop只运行在一个线程(进程)中也可以同时处理多个用户请求。相信你已经对事件驱动编程有一个清晰的认知了,那么接下来的问题就是事件驱动、事件驱动,那么这个事件也就是event该怎么获取呢?
 

(编辑:百科站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章