Scala的阻塞上下文似乎在混合阻塞/非阻塞作业中表现不佳。为什么?

dev*_*ium 5 concurrency multithreading scala fork-join

我试图弄清楚blocking构造。虽然还不清楚它的内部工作原理,但是我得到的一般想法是,只要我使用Scala的全局线程池,用blocking上下文包装我的代码就可以确保线程池会为这项工作创造额外的空间(因为它不受CPU限制)。

  (1 to 1000).foreach { i =>
    Future {
      println(i)
      Thread.sleep(100 * 1000)
    }
  }
Run Code Online (Sandbox Code Playgroud)

将迅速显示只能同时运行8个作业,而

  (1 to 1000).foreach { i =>
    Future {
        blocking {
            println(i)
            Thread.sleep(100 * 1000)
        }
    }
  }
Run Code Online (Sandbox Code Playgroud)

将显示现在我们大约有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)
        }
    }
  }
Run Code Online (Sandbox Code Playgroud)

只会再次显示8个同时进行的作业-阻止的作业不会立即执行。

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

Bri*_*hon 4

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

您可以将第二个片段视为这样工作:

  1. 第一个未来已被创造并开始。
  2. 第一个 future 通过调用来表明它正在阻塞blocking,因此该实现为更多 future 腾出了空间。
  3. 同时,在主线程上,创建并启动了第二个 future。
  4. ...

而你的最后一个片段的工作原理如下:

  1. 第一个未来已被创造并开始。它并不表明它正在阻塞。
  2. 第二个未来也做同样的事情。
  3. ...
  4. 第 9 个 future 已创建,但尚未启动,因为有 8 个非阻塞 future。
  5. ...
  6. 第 1001 个 future(第二个循环中的第一个)已创建,但尚未启动,因为有 8 个非阻塞 future。由于它尚未启动,因此它永远没有机会通过调用 来告诉实现它正在阻塞blocking