Multithreading Scala';s阻塞上下文不';t似乎在混合阻塞/非阻塞作业中做得不好。为什么?

Multithreading Scala';s阻塞上下文不';t似乎在混合阻塞/非阻塞作业中做得不好。为什么?,multithreading,scala,concurrency,fork-join,Multithreading,Scala,Concurrency,Fork Join,我试图理解阻塞构造。虽然还不完全清楚它内部是如何工作的,但我得到的总体想法是,只要我使用Scala的全局线程池,用阻塞上下文包装我的代码,就可以确保线程池为这个作业创建额外的空间(因为它不受CPU限制) 将很快显示只有8个作业可以同时运行,而 (1 to 1000).foreach { i => Future { blocking { println(i) Thread.sleep(100 * 1000)

我试图理解
阻塞
构造。虽然还不完全清楚它内部是如何工作的,但我得到的总体想法是,只要我使用Scala的全局线程池,用
阻塞
上下文包装我的代码,就可以确保线程池为这个作业创建额外的空间(因为它不受CPU限制)

将很快显示只有8个作业可以同时运行,而

  (1 to 1000).foreach { i =>
    Future {
        blocking {
            println(i)
            Thread.sleep(100 * 1000)
        }
    }
  }
将显示现在我们有大约250个同时工作。哇! 让我措手不及的是

  (1 to 1000).foreach { i =>
    Future {
      println(i)
      Thread.sleep(100 * 1000)
    }
  }

  ('a' to 'z').foreach { c =>
    Future {
        blocking {
            println(c)
            Thread.sleep(100 * 1000)
        }
    }
  }
将再次仅显示8个同时执行的作业--阻塞作业不会立即执行


为什么会这样?
阻塞上下文的内部机制到底是什么?

您的第一个循环启动8个线程,并阻塞,因为它需要在完成之前再启动992个未来


不确定是什么“让你措手不及”。第一个
foreach
调用完成后,它将转到第二个,然后再启动26个。

阻塞
仅在您输入阻塞上下文后生效。由于有8个非阻塞期货正在运行,因此它不会启动任何新期货,因此它们无法进入阻塞上下文。换句话说,Scala在开始执行之前并不“知道”它们正在阻塞

您可以认为第二个代码段的工作方式如下:

  • 第一个未来被创造并开始
  • 第一个future通过调用
    blocking
    ,表示它正在阻塞,因此该实现为更多的未来腾出了空间
  • 同时,在主线上,第二个未来被创建并启动
  • 而您的最后一个代码段的工作方式如下:

  • 第一个未来被创造并开始。它并不表示它正在阻塞
  • 第二个未来也是如此
  • 第9个期货已创建,但尚未启动,因为有8个非阻塞期货
  • 创建了第1001个期货(第二个循环中的第一个期货),但没有启动,因为有8个非阻塞期货。因为它没有启动,所以它从来没有机会通过调用
    blocking
    来告诉实现它正在阻塞

  • 你好请重读我写的东西。如果我运行第二个代码段,它将创建250个同时运行的线程。它在最后一个代码段中没有这样做。请重新阅读我写的:)它在最后一个代码段中没有这样做,因为最后一个代码段从第一个代码段开始。。。它启动8个线程并等待它们完成,然后才能继续。当第一个
    阻塞
    未来
    最终运行时,所有26个将同时运行。您好,感谢您的回复!你的解释有道理。不幸的是,这使得
    阻塞{}
    上下文有点无用——如果我有一个已经阻塞的线程池,那么我就卡住了。我认为我最好有两个线程池,一个是无限的IO线程池(或有非常高的数量限制),另一个是固定的线程池,用于CPU限制的任务。@EnglumeDelyum真正的问题是
    Future
    是为短时间、非阻塞操作而设计的。如果您想要一个“线程池”,那么请查看任务库或Actors。在CPU受限的情况下,添加更多线程可能会有问题,因为它可以通过创建更多线程来降低CPU受限工作负载的性能,这些线程将被分配时间片。因此,
    阻塞
    通常只用于IO绑定或等待部分,而对于CPU绑定的部分,通常最好将工作划分为多个异步步骤。我的问题其实很简单:我有一个大的多线程(服务器)应用程序,它将产生异步任务的负载。其中大多数都会涉及一些外部调用(IO)。我应该为CPU使用固定线程池(具有计算机的核心计数)+为IO调用使用无限缓存线程池,还是应该始终使用Scala的全局线程池,利用
    阻塞
    上下文?一般的想法是,如果您想要长时间运行的异步进程,您可以使用任务库或参与者,简称
    Future
    ,非阻塞操作。
      (1 to 1000).foreach { i =>
        Future {
          println(i)
          Thread.sleep(100 * 1000)
        }
      }
    
      ('a' to 'z').foreach { c =>
        Future {
            blocking {
                println(c)
                Thread.sleep(100 * 1000)
            }
        }
      }