【问题标题】:Thread vs Event Loop - network programming (language agnostic) [closed]线程与事件循环 - 网络编程(与语言无关)[关闭]
【发布时间】:2014-06-27 10:24:46
【问题描述】:

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

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

就并发性和可扩展性而言,线程或事件(想想 Python 中的 Twisted)在这种情况下会更好地工作吗?事件模型似乎更有意义,但我想对你们进行投票。数据进来 -> 处理数据 -> 等待更多数据。如果“做事”的计算量非常大怎么办?如果“做的东西”是非常 IO 密集型的(例如插入数据库)。这会阻止事件循环吗?每种方法的优缺点是什么?

【问题讨论】:

    标签: python multithreading events twisted


    【解决方案1】:

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

    twisted、tornado、gevent 等异步编程库非常擅长并行处理大量 I/O。如果您的工作负载涉及许多客户端连接、执行轻量级 CPU 操作和/或大量 I/O 操作(如 db 读/写),或者如果您的客户端正在建立主要用于 I/O 的持久连接(想想 WebSockets),那么异步库将非常适合您。大多数用于 Python 的异步库都有用于流行 DB 的异步驱动程序,因此您将能够与它们进行交互而不会阻塞您的事件循环。

    如果您的服务器要执行大量 CPU 密集型工作,您仍然可以使用异步库,但必须了解每次执行 CPU 工作时,事件循环都会被阻塞。没有其他客户可以做任何事情。但是,有一些方法可以解决这个问题。您可以使用线程/进程池来处理 CPU 工作,然后异步等待响应。但显然这会使您的实现有点复杂。

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

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

    【讨论】:

    • 你好,为什么说“每次做CPU工作,事件循环都会被阻塞”。 ?有什么文件可以解释吗?
    【解决方案2】:

    我认为您的问题属于“取决于”类别。

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

    除此之外,当您查看进程、线程和事件时,操作系统也有不同的优势和劣势。在 unix 上正确的东西与 windows 不同。

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

    这些项目很复杂,没有工具可以简单地消除复杂性,因此您有两种选择:

    1. 让操作系统处理尽可能多的复杂性,让程序员的生活更轻松,但以机器效率为代价
    2. 让程序员承担尽可能多的复杂性,以便他们可以优化设计并尽可能多地从机器中挤出性能,但代价是需要更高端程序员的更复杂的代码。

    选项 1 通常最好通过将任务分解为线程或进程来完成,每个线程/进程使用一个阻塞状态机

    选项 2 通常最好通过将所有任务多路复用到一个进程中并使用事件系统的 OS 挂钩来完成。 (select/poll/epoll/kqueue/WaitForMultipleObjects/CoreFoundation/libevent等..)

    根据我的经验,项目框架/内部架构通常归结为手头程序员的技能(以及项目对硬件的预算)。

    如果您有具有操作系统内部背景的程序员:Twisted 非常适合 python,Node.js 适合 Javascript,libevent/libev 适合 C 或 C++。您还将获得可以轻松扩展的超高效代码,尽管您将面临一场噩梦,试图雇用更多的程序员

    如果您有新手程序员,并且您可以将资金投入到大量云服务中,那么将项目分解为多个线程或进程将为您提供使某些东西正常工作的最佳机会,尽管扩展最终会成为一个问题。

    总而言之,对于具有多次迭代的项目,最明智的模式是在简单的阻塞工具(flask)中进行原型设计,然后重新编写成更难/更可扩展(扭曲)的东西,否则你会陷入困境经典的Premature optimization is the root of all evil陷阱

    【讨论】:

      【解决方案3】:

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

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

      当连接很短并且断开后进行重处理时,两个模型相互称重。

      【讨论】:

        猜你喜欢
        • 2013-06-19
        • 1970-01-01
        • 1970-01-01
        • 2017-07-03
        • 2011-08-20
        • 1970-01-01
        • 2010-10-01
        • 1970-01-01
        • 2013-04-23
        相关资源
        最近更新 更多