【问题标题】:What profit of using multi-threaded coroutines?使用多线程协程有什么好处?
【发布时间】:2022-01-06 09:52:01
【问题描述】:

我使用协程已经很长时间了,但我仍然不完全理解,为什么我需要更喜欢多线程协程而不是单线程协程。

当多线程协程的数量小于或等于物理线程数量时,我可以清楚地看到使用多线程协程的好处。但是如果我们的任务比物理线程多,为什么不宁愿只使用一个协程线程呢?

我将澄清最后一个问题:为什么 10 个线程的协程比只有一个线程的多个协程更好?

【问题讨论】:

  • 我不得不承认我跟不上你的思路。首先,如果你的任务比物理内核少,你甚至不需要协程,原生线程就可以了。其次,当你有 more 个协程时它需要 less 个线程的想法,完全摆脱了我所有认真的尝试来理解你的角度。
  • @MarkoTopolnik 我也很困惑,但我的印象是 OP 假设协程以某种方式直接调度在 CPU 内核上。所以如果有足够多的协程来占据所有的核心,就不需要线程了。如果协程比核心少,那么 MT 可能会以某种方式......错误,我不知道,通过利用免费核心使协程运行得更快?当然,这根本没有任何意义。协程不是调度在 CPU 内核上,而是调度在线程上。然后在 CPU 内核上调度线程。
  • 我知道协程是在线程中解决的,我只是觉得有些协程可以在一个线程中同时工作,不明白为什么我需要的线程多于一个。现在看来我明白它的工作原理了,乔佛里给了我一个很好的答案。
  • 我猜你的困惑源于“并发”这个词的使用方式,它与“并行”是一个不同的技术术语。您可以在一个线程上拥有多个并发协程,但它们不会并行运行。并行执行只能在单独的 CPU 内核上进行。

标签: multithreading kotlin kotlin-coroutines


【解决方案1】:

协程是计算单元(如任务)。它们被分派到实际线程的方式与您拥有多少协程是正交的。您可以使用单线程调度程序或多线程调度程序,根据这一点,您的协程将被不同地调度。

多线程协程并不意味着每个协程 1 个线程。您可以在 8 个线程上调度 100 个协程。

但是如果我们有比物理线程更多的任务,为什么我们不宁愿只使用一个协程线程呢?

这个问题有多个部分。

首先,如果您的任务多于逻辑内核,您仍然可以将所有这些任务分派到正确数量的线程上。您不必完全放弃多线程。这实际上正是Dispatchers.Default 的意义所在:将任意数量的协程分派到与您拥有的硬件线程(逻辑核心)数量相等的有限数量的线程上。关键是尽可能地利用所有硬件,而不会浪费内存(以及内存)。

其次,并非每个任务都受 CPU 限制。一些 I/O 操作会阻塞线程(网络调用、磁盘读/写等)。当线程在 I/O 上被阻塞时,它不会使用 CPU。如果您有 8 个逻辑核心,则仅使用 8 个线程进行 I/O 将不是最理想的,因为当一些线程被阻塞时,CPU 无法运行其他任务。使用更多线程,它可以(以一些内存为代价)。这就是Dispatchers.IO的重点,它可以根据需要创建更多的线程,并且可以超过逻辑核心的数量(在合理的限度内)。

为什么 10 个线程的协程比只有一个线程的多个协程更好?

假设您有 100 个协程要调度。

仅使用一个线程来运行这些协程意味着在给定时间最多只有 1 个内核在做这项工作,因此不会并行发生任何事情。这意味着所有其他内核都处于空闲状态,这是次优的。更糟糕的是,协程完成的任何 I/O 操作都会阻塞这个唯一的线程,并阻止 CPU 在我们等待 I/O 时执行任何操作。

使用 10 个线程,如果您的硬件足够,您实际上可以同时执行 10 个协程,这可以快 10 倍(如果您的协程没有相互依赖关系)。

如果您的协程受 CPU 限制,则使用 100 个线程不会有什么好处,但如果您有一堆 I/O 任务(如我们所见),则可能很有用。也就是说,您使用的线程越多,消耗的内存就越多。因此,即使有大量的 I/O 操作,您也必须在吞吐量和内存之间找到平衡点,您不希望产生数百万个线程。

简而言之,无论有没有协程,使用多线程仍然具有相同的优势:它允许尽可能多地利用您的硬件资源。使用协程只是一种更简单的方式来定义任务、将它们分派到线程上、表达依赖关系、避免不必要地阻塞线程等等。

【讨论】:

    猜你喜欢
    • 2011-11-05
    • 1970-01-01
    • 2016-05-21
    • 1970-01-01
    • 2020-04-24
    • 2012-01-12
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    相关资源
    最近更新 更多