75

I came across the scala.concurrent.blocking method, and according to the Scala documentation this is...

Used to designate a piece of code which potentially blocks, allowing the current BlockContext to adjust the runtime's behavior. Properly marking blocking code may improve performance or avoid deadlocks.

I have some doubts:

  • what is the factor with which new threads will be spawned?
  • Is this applicable only for scala.concurrent.ExecutionContext.Implicits.global execution context or for user-created execution contexts as well?
  • What happens if I wrap any executable with blocking { ... }?
  • Any practical use case where we should use this construct.
Flow
  • 23,572
  • 15
  • 99
  • 156
Sourav Chandra
  • 952
  • 2
  • 7
  • 11

1 Answers1

64
  1. The new threads are spawned in the fork/join pool when it detects that all the threads in the fork/join pool are waiting on each other using the join construct, and there is more work to be completed that could potentially finish one of the threads. Alternatively, if one of the ForkJoinWorker threads is executing code that blocks other than by using join, it can notify the pool using ManagedBlockers.
  2. It is potentially applicable to any kind of execution contexts -- it serves as a notification to the ExecutionContext implementation that the code executed by a worker thread is potentially blocking on some condition, and that this condition might be resolved by computing something else using some other thread. The execution context may or may not act on this. In the current (2.10, 2.11) implementation, blocking will work only with the default global execution context.
  3. If you wrap any executable with blocking you will induce a bit of runtime overhead, so don't always do it.
  4. If you have a computation that lasts a long time, e.g. seconds or minutes, or you are waiting on a future to complete using Await, or you are waiting on a monitor's condition to become resolved, and this condition can be resolved by some other task/future that should execute on the same execution context -- in all these cases you should use blocking.

EDIT:

Consider taking a look at Chapter 4 in the Learning Concurrent Programming in Scala book.

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
axel22
  • 32,045
  • 9
  • 125
  • 137
  • 2
    What we gain from using `blocking`? Should this be used only with futures(not in main thread)? – maks Nov 25 '13 at 22:30
  • You can use it anywhere, but it will only have an effect on the worker threads, i.e. computations executed with `Future { ... }`. – axel22 Nov 25 '13 at 22:48
  • Already found an answer here http://stackoverflow.com/questions/13097754/asynchronous-io-in-scala-with-futures#answer-13099594 – maks Nov 26 '13 at 00:08
  • Is it still true that for Scala 2.11.3+, blocking will work only with the default global execution context? Is there a simple way to create another ExecutionContext that supports blocking, but perhaps tuned for a specific use case? For example, from http://blog.jessitron.com/2014/02/scala-global-executioncontext-makes.html - "When should you not use it? When you're writing an asynchronous library, or when you know you're going to do a lot of blocking, declare your own thread pool. Leave the global one for everyone else." – Mike Slinn Oct 20 '14 at 21:03
  • `blocking` is a hint to the thread pool that the block of code is potentially blocking. As far as I know, implementations other than the global execution context still ignore this hint. If you want the execution context to act on this hint, you have to implement your own execution context. The easiest way to do this is to wrap `ForkJoinPool` and implement the `execute` method so that it sets your own `BlockContext` object, or that the worker threads are `BlockContext` objects. See: https://github.com/scala/scala/blob/v2.11.3/src/library/scala/concurrent/BlockContext.scala – axel22 Oct 22 '14 at 18:53
  • Aleksandar, the code for BlockContext that you referenced does not reference ForkJoinPool in an obvious way. What is the connection? – Mike Slinn Oct 23 '14 at 08:22
  • The code I referenced shows the `BlockContext` trait that you need to implement to make the `blocking` calls do some custom logic for the current execution context. You would need to: 1. either instantiate the `ForkJoinPool` underlying the execution context with a custom `ForkJoinWorkerThreadFactory` which creates threads that mix in `BlockContext` OR implement the `execute` method to call the `withBlockContext` before executing the `Runnable`. See http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJoinPool.ForkJoinWorkerThreadFactory.html – axel22 Oct 23 '14 at 19:34
  • 2. Implement the `blockOn` method so that it creates a `ManagedBlocker` and calls its `block` method (see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJoinPool.ManagedBlocker.html). – axel22 Oct 23 '14 at 19:34
  • Explanation: the `blocking` call invoked by the clients will check if the current thread is a `BlockContext` or if the thread local with the current context is set to a `BlockContext` object. In either case, the `blockOn` method is invoked on the `BlockContext` object. Depending on the `ExecutionContext` implementation, the `blockOn` method can do different things. If the `ExecutionContext` is a `ForkJoinPool`, it needs to instantiate a `ManagedBlocker` object, and call its `block` method. The `ManagedBlocker` is specific to fork/join pool, and it lets it know that a blocking call started. – axel22 Oct 23 '14 at 20:48
  • 1
    Seems like there would be general interest in 'best practices' code example(s) of how to do this. – Mike Slinn Oct 23 '14 at 21:54
  • When I catch some time, I'll write something up about this (e.g. a blog post). In the meantime, I can recommend Chapter 4 in this book: https://www.packtpub.com/application-development/learning-concurrent-programming-scala – axel22 Jan 10 '15 at 15:59
  • "If you have a computation that lasts a long time" - I think this situation is debatable. Thread doing a computation is not blocked, and spawning a new thread by the pool in such situation can lead to more threads than desired. – Suma Nov 18 '15 at 12:09
  • 2
    @Suma I agree that a long-running computation does not fit under the definition that axel22 gave, particularly point 2. Long running computation dies not benefit from work computed in other threads, rather contrary it'll get less resources to complete. I just ran into situation where we used akka EC (ForkJoin) with blocking{} and ran out of threads because we had 2k of these long computations not completed in time. I'd like to hear a justification how blocking{} benefit long-running independent computations. – Tim Aug 30 '16 at 19:31