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

时间:2019-03-26 20:39:07

标签: multithreading scala concurrency fork-join

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

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

将迅速显示只能同时运行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个同时进行的作业-阻止的作业不会立即执行。

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

2 个答案:

答案 0 :(得分:4)

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

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

  1. 创建并开始了第一个未来。
  2. 第一个Future表示通过调用blocking阻止了它,因此该实现为更多的期货腾出了空间。
  3. 同时,在主线程上,创建并启动了第二个Future。
  4. ...

最后一个代码段的工作方式如下:

  1. 第一个未来已创建并开始。它并不表示它正在阻塞。
  2. 第二个未来也是如此。
  3. ...
  4. 第9个期货已创建但尚未开始,因为有8个非阻塞性期货。
  5. ...
  6. 已创建但未启动第1001个期货(第二个循环中的第一个),因为有8个非阻塞期货。由于尚未启动,因此它永远没有机会通过调用blocking来告知实现它正在阻塞。

答案 1 :(得分:0)

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

不确定“使您措手不及”是什么意思。第一个foreach通话结束后,它将继续进行第二个通话,然后再开始26个通话。