When is multi-threading not a good idea?我最近正在开发一个通过以太网和串行发送和接收消息的应用程序。 然后,我受命添加对DIO离散量的监视。 我说
然而,这一决定证明是糟糕的。 有时主线程会在发送和接收串行消息之间中断。 这种中断会打乱时间,而且消息会丢失(永远)。 我找到了另一种无需使用其他线程即可监视DIO的方法,并且以太网和串行通信已恢复为正确的功能。 然而,整个惨败让我思考。 他们是否有关于何时不使用多线程的一般指导方针,和/或在使用多线程不是一个好主意的情况下,是否有人再有其他示例? **编辑:基于您的评论,并且在浪费互联网的信息之后,我撰写了一篇博客文章,标题为"多线程何时不是一个好主意? 总体而言,如果您将多个线程用于非冻结桌面应用程序和任何其他通用答案,则如果您使用的是单核计算机,则由于线程彼此中断,会使应用程序变慢。 为什么?因为有硬件开关。硬件总共要花费时间在线程之间进行切换。在多核设备上,继续并为每个核使用1个线程,您将大大看到上升的趋势。 改写一句老话:程序员遇到了问题。他想:"我知道,我会使用线程。"现在程序员有两个问题。 (通常归因于JWZ,但似乎早于他使用正则表达式时使用它。) 一个好的经验法则是"不要使用线程,除非有非常令人信服的理由使用线程。"多个线程正在自找麻烦。尝试找到一种无需使用多个线程即可解决该问题的好方法,并且只有避免使用该线程会比使用线程付出额外的精力多,否则只能使用线程。另外,如果您在多核/多CPU的计算机上运行,??请考虑切换到多个线程,并且单线程版本的性能测试表明您需要额外的内核的性能。 多线程是一个坏主意,如果:
实际上,多线程是不可扩展的,并且很难调试,因此在任何情况下都可以避免使用多线程。在少数情况下,强制执行是必不可少的:当多CPU的性能很重要时,或者当您处理的服务器上有很多客户端需要很长时间才能回答时。 在任何其他情况下,都可以使用诸如队列+ cron作业之类的替代方法。 多线程是不好的,除非在单线程情况下是好的。这种情况是 如果缺少这两个条件之一或全部,则多线程将不是一个成功的策略。 如果工作不受CPU约束,则您不是在等待线程完成工作,而是等待某些外部事件(例如网络活动)来完成该过程。使用线程,会在线程之间增加上下文切换的开销,同步的开销(互斥变量等)以及线程抢占的不规则性。最常见的替代方法是异步IO,其中单个线程侦听多个io端口,并在现在碰巧准备就绪的任何一个上进行操作,一次一次。如果偶然地所有这些慢速通道都同时准备就绪,则似乎您会遇到速度变慢的情况,但实际上这很少是正确的。通常,单独处理每个端口的成本与清空每个通道时同步多个线程上的状态的成本相比或更高。 许多任务可能是受计算限制的,但使用多线程方法仍然不切实际,因为该过程必须在整个状态上进行同步。这样的程序无法从多线程中受益,因为无法同时执行任何工作。幸运的是,大多数需要大量CPU的程序都可以并行化到某个级别。 您可能想看看Dan Kegel的" C10K问题"网页,该网页处理多个数据源/接收器。 基本上,最好使用最少的线程,该线程可以在大多数操作系统的某些事件系统中(或者在Windows中使用IOCP异步执行)在套接字中完成。 当您遇到操作系统和/或库不提供非阻塞方式进行通信的情况时,最好在报告回到同一事件循环时使用线程池来处理它们。 布局示例图:
我写的一个最近的应用程序必须使用多线程(尽管线程数不是无限的),我必须通过两个协议在多个方向上进行通信,还要监视第三个资源的更改。这两个协议库都需要一个线程来运行相应的事件循环,并且在考虑了这些事件后,很容易为资源监视创建第三个循环。除了事件循环要求之外,通过线路传递的消息还具有严格的时序要求,一个循环不会冒阻塞另一个循环的风险,而使用多核CPU(SPARC)可以进一步缓解这种情况。 关于是否应该将每个消息处理视为线程池中分配给线程的工作,还有进一步的讨论,但最后这是一个扩展,不值得做。 如果可能,仅在可以将工作划分为定义良好的工作(或一系列工作)时才考虑所有线程,以使语义相对易于记录和实现,并且可以对线程设置上限。您使用且需要进行交互的线程数。最好应用此功能的系统几乎是消息传递系统。 如果您需要保证精确的物理时序(例如您的示例),那么多线程不是一个好主意。其他缺点包括线程之间的密集数据交换。我要说的是,如果您不太在意它们的相对速度/优先级/定时,那么多线程对于真正的并行任务很有用。 使用线程的更多可能原因: 在密集处理期间保持GUI响应并不总是需要其他线程。一个回调函数通常就足够了。 如果以上方法均不适用,并且由于某些原因我仍然希望并行化,那么我更愿意在可能的情况下启动一个独立的进程。 我会说多线程通常用于: 因此,如果您不解决这些问题之一,则添加线程不太可能使您的生活更轻松。实际上,几乎可以肯定,它将变得更加困难,因为正如其他人所提到的;调试多线程应用程序比单线程解决方案的工作量大得多。 安全性可能是避免使用多个线程(在多个进程上)的原因。有关多进程安全功能的示例,请参见Google chrome。 原则上,每次调用者在队列中等待时都没有开销。 多线程具有可伸缩性,可以让您的UI保持其响应能力,同时在后台执行非常复杂的事情。我不了解其他响应在哪里获取有关多线程的信息。 什么时候不应该使用多线程,这是对您的问题的误导性问题。您的问题是这样的:为什么对我的应用程序进行多线程处理会导致串行/以太网通信失败? 该问题的答案将取决于实现,应在另一个问题中进行讨论。我知道一个事实,即可以在多线程应用程序中同时进行以太网和串行通信,而无需执行任何其他任务,而不会造成任何数据丢失。 不使用多线程的原因之一是: 使用多线程的原因有: 多线程编程的三种基本方法可以轻松实现线程安全-您只需使用一种方法即可成功: 线程问题的常见根源是用于同步数据的常用方法。让线程共享状态,然后在所有适当的位置实现锁定是设计和调试复杂性的主要来源。获得锁定权以平衡稳定性,性能和可伸缩性始终是一个很难解决的问题。即使是最有经验的专家也会经常出错。处理线程的替代技术可以减轻这种复杂性。 Clojure编程语言实现了一些用于处理并发的有趣技术。 流程是否平行?性能真的值得关注吗?是否像Web服务器一样有多个执行"线程"?我认为答案是有限的。 |