何时应该使用线程?

18

就我个人而言,理想的线程数量是3:一个用于UI,一个用于CPU资源,一个用于IO资源。

但我可能是错的。

我刚刚开始接触它们,但我一直使用一个线程用于UI,另一个线程用于其他所有任务。

何时应该使用线程以及如何使用?我如何知道是否应该使用它们?


3
我的回答可能没有那么详细,但是关于用户界面(UI)方面,如果在适当的情况下使用多个线程,即使吞吐量较低,在加载网页浏览器中的选项卡时每个选项卡使用一个线程,可以给用户呈现出更高响应时间的外观。当然这是一个具体问题具体分析的过程。 - Phil
5个回答

17

很不幸,使用线程没有硬性规定。如果你拥有太多的线程,处理器将花费大量时间在它们之间的生成和切换上。使用太少的线程会导致你的应用程序无法获得所需的吞吐量。此外,使用线程并不容易。像C#这样的语言使它更加容易,因为你有像ThreadPool.QueueUserWorkItem这样的工具。这使得系统能够管理线程的创建和销毁。这有助于减轻创建新线程以传递任务的开销。你必须记住,创建一个线程不是一个你可以"免费"得到的操作。启动一个线程是有成本的,因此应该始终考虑到这一点。

根据你用来写应用程序的语言,你将决定需要多么担心使用线程。

我发现最常需要显式创建线程的情况是:

  • 异步操作
  • 可以并行化的操作
  • 持续运行的后台操作

9
答案完全取决于您计划做什么。然而,对于CPU资源来说,多线程是一个糟糕的选择 - 在零售CPU中,您的CPU可能有多达六个核心,加上超线程,大多数CPU将拥有两个或更多核心。在这种情况下,您应该有与CPU核心数量相同的线程,再加上一些用于调度失误。整个CPU不是单线程的怪物,它可能有许多核心并需要多个线程才能实现100%的利用率。
只有当您的目标人群几乎都拥有多核心(如当前的台式机/笔记本电脑市场),并且已确定一个核心的性能不足时,您才应该使用线程。

5
SQLite FAQ中得知:“线程很邪恶。除非你必须使用它们,否则避免使用。” 如果确实需要使用,请采取措施避免通常的伤害。使用线程池执行没有相互依赖关系的细粒度任务,并使用GUI框架提供的工具将结果返回到UI。避免在长时间运行的线程之间共享数据;使用消息队列传递信息(并同步)。更奇特的解决方案是使用像Erlang这样的语言,它们明确针对细粒度并行性而设计,同时不会牺牲安全性和可读性。并发本身对于计算的未来至关重要;线程只是一种可怕、破损的表达方式。

8
不要教条。线程本身并不是邪恶的。将UI和后台活动线程分开是一个很好的模式,它可以提供更好的用户体验。 - Andrey
2
如果可以不使用线程,那就尽量避免使用——这样做肯定会使测试和调试变得更加容易,并且可能会使产品在长期内更加稳定。如果你必须使用线程,那就要明智地使用。 - Paul R
2
@Andrey,我认识的最聪明的人对在他们的代码中使用线程最不乐观。请跟随我提供的PDF链接。 - Marcelo Cantos
1
@Marcelo Cantos- 谢谢,我之前已经读了这个PDF三次。你说不要使用线程是什么意思?我有长时间的IO操作,你有什么建议?等待并阻塞其他所有操作吗?这很愚蠢,因为线程处于等待状态,没有做任何有用的事情。另一个例子:你有多核处理器,并且进行CPU密集型操作。你建议只使用一个内核,因为线程是邪恶的吗? - Andrey
2
@Marcelos,PDF并没有说不要使用并行处理,只是不要使用线程编程模式来实现。你可能需要明确表明,你只是支持通过顺序进程之间的消息传递来实现并行处理,而不是不支持并行处理。 - Michael Aaron Safyan
我同意这个观点。我的经验法则是:如果你认为线程很酷很好玩,那说明你不理解它们,也不应该使用它们。如果你认为线程有时是必要的恶,那么只有在没有其他选择的情况下才能使用它们。 - dan-gph

5

Herb Sutter在Dr. Dobb's Journal上写了一篇文章,讨论了并发的三个支柱。这篇文章非常好地分解了哪些问题适合使用线程构造来解决。


3
“理想的线程数量”取决于您特定的问题以及您可以利用多少并行性。如果您有一个问题是“尴尬地并行”的,因为它可以细分为独立问题,并且它们之间需要很少或没有通信,并且您拥有足够的核心以实现真正的并行处理,则使用多少线程取决于问题大小、缓存行大小、上下文切换和生成开销以及各种其他在预先计算之前实际上非常难确定的事情。对于这种情况,您确实需要进行一些分析,以便选择最佳的分片/分区方式跨越线程。但通常不要使用比内核更多的线程。此外,如果存在大量同步,则实际上使用线程可能会导致性能损失。这高度依赖于特定问题以及各个步骤之间的相互依赖程度。作为指导原则,您需要了解生成线程和线程同步是昂贵的操作,但如果通信和其他形式的同步最小化,那么并行执行计算可以增加吞吐量。您还应该注意,如果您的线程最终使共享缓存行无效,则线程可以导致非常糟糕的缓存性能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接