线程与事件循环网络编程(语言无关)

2021-01-17 13:40:24 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在编写一个简单的守护进程来接收来自N个移动设备的数据。设备将轮询服务器,并以简单的JSON格式发送所需的数据。一般来说,服务器将接收数据,然后对其进行“处理”。在

我知道这个话题已经被击败了很多次,但我很难理解利弊。在

就并发性和可伸缩性而言,线程或事件(Python中的Twisted)是否能更好地解决这种情况?活动模式似乎更有意义,但我想对你们进行民意调查。数据进入->处理数据->等待更多数据。如果“do-stuff”是一个计算密集型的东西呢?如果“do-stuff”是非常IO密集型的(比如插入到数据库中),该怎么办。这会阻止事件循环吗?每种方法的优缺点是什么?在

3条回答
网友
1楼 ·

我认为你的问题属于“视情况而定”的范畴。在

在线程/进程/事件方面,不同的语言有不同的优缺点(python在线程方面有一些特殊的弱点,与global interpreter lock有关)

除此之外,当你看进程、线程和事件时,操作系统也有不同的优缺点。在unix上正确的做法与windows不同。在

话虽如此,我梳理多方面IO项目的方式是:

These projects are complex, no tool with simply make the complexity go away, therefor you have two choices on how you can deal:

  1. Have the OS deal with as much complexity as possible, making life easier for the programers, but at the cost of machine efficiency
  2. Have the programer take on as much complexity as is practical so that they can optimize the design and squeeze as much performance out machine as possible, at the cost of more complex code that requires significantly higher-end programers.

Option 1 is normally best accomplished by breaking apart the task into threads or processes with one blocking state-machine per thread/process

Option 2 is normally best accomplished by multiplexing all the tasks into one process and using the OS hook for an event system. (select/poll/epoll/kqueue/WaitForMultipleObjects/CoreFoundation/ libevent etc..)

根据我的经验,项目框架/内部架构(architecture)通常取决于手头程序员的技能(以及项目在硬件方面的预算)。在

如果你有一个有操作系统内部背景的程序员:Twisted对python非常有用,节点.js对于JavaScript来说非常有用,LiBev/LeBeV将对C或C++有很大的帮助。你最终也会得到高效的代码,这些代码可以很容易地扩展,尽管你会在试图雇佣更多的程序员时做噩梦

如果你有新手程序员,你可以把钱投入到大量的云服务中,那么将项目分成多个线程或进程将给你最好的机会让某些东西发挥作用,尽管扩展最终会成为一个问题。在

总而言之,对于一个有多个迭代的项目来说,最明智的模式是在简单的块工具(flask)中建立原型,然后重新编写更难/更具伸缩性(twisted)的东西,否则就会陷入经典的Premature optimization is the root of all evil陷阱

网友
2楼 ·

连接方案的选择也很重要。您希望有多少并发连接?客户机保持连接多长时间?在

如果每个连接都绑定到一个线程,那么许多并发连接或非常长时间的连接(如websockets)将阻塞系统。对于这些场景,基于事件循环的解决方案会更好。在

当连接较短,断开连接后进行重处理时,两种模型都会互相权衡。在

网友
3楼 ·

我只能在Python上下文中回答,因为这是我大部分经验所在。答案可能会根据你选择的语言而有所不同。例如,Python在并行化I/O密集型操作方面比CPU密集型操作要好得多。在

异步编程库如twisted、tornado、gevent等非常擅长并行处理大量的I/O。如果您的工作负载涉及到许多客户机连接、执行少量CPU操作和/或大量I/O操作(如db读/写),或者如果您的客户机主要用于I/O(想想WebSocket),那么异步库将非常适合您。Python的大多数异步库都有针对流行DBs的异步驱动程序,因此您可以在不阻塞事件循环的情况下与它们交互。在

如果您的服务器将要执行大量CPU密集型工作,您仍然可以使用异步库,但必须了解,每次执行CPU工作时,事件循环都会被阻塞。没有其他客户会做任何事情。然而,有办法解决这个问题。您可以使用线程/进程池来将CPU工作分配出去,只需异步地等待响应。但很明显,这会使您的实现稍微复杂一些。在

在Python中,使用线程实际上并不能为CPU操作买单,因为在大多数情况下,一次只能运行一个线程,所以您并没有真正从拥有多核CPU的好处中获益(google“Python GIL”了解更多信息)。忽略Python特有的线程问题,线程可以让您完全避免“阻塞事件循环”问题,而且线程代码通常比异步代码更容易理解,特别是如果您还不熟悉异步编程是如何工作的。但是,您还必须处理常见的线程问题(同步共享状态等),而且它们的伸缩性不如异步I/O对许多客户端的影响(请参见http://en.wikipedia.org/wiki/C10k_problem

这两种方法在生产中都得到了非常成功的应用,因此您需要决定哪些方法更适合您的需求/偏好。在

相关问题