Reputation: 15385
Let's say I have the following set of code that does something in a Future:
1 to 10 foreach {
case x => Future { x + x }
}
Assuming that I give the default ExecutionContext to this piece of code, I know what happens in the background, but what I want to know is how is the handling of the Future actually done? I mean there should be some thread or a set of threads that should potentially be waiting for the Future to finish? Are these threads blocked? blocked in the sense where they are literally waiting for the Future to finish?
Now in the following scenario:
val x: Future[MyType] = finishInSomeFuture()
Assuming that x has a timeout that I can call like this:
Future {
blocking {
x.get(3, TimeOut.SECONDS)
}
}
Am I really blocking? Is there a better way to timeout asynchronously?
EDIT: How different or how better is the following Timeout better than the blocking context that I defined above?
object TimeoutFuture {
def apply[A](timeout: FiniteDuration)(block: => A): Future[A] = {
val prom = promise[A]
// timeout logic
Akka.system.scheduler.scheduleOnce(timeout) {
prom tryFailure new java.util.concurrent.TimeoutException
}
// business logic
Future {
prom success block
}
prom.future
}
}
Upvotes: 1
Views: 1982
Reputation: 10882
Let's say I have the following set of code that does something in a Future:
1 to 10 foreach { case x => Future { x + x } }
...
Your piece of code creates ten Futures
that are immediately set for execution using threads provided by implicit ExecutionContext. As you don't store the references to your futures, and don't await for their execution, your main thread (where your foreach
is defined) doesn't block and continues its execution immediately. If that piece of code was in the end of the main
method, then, depending on whether ThreadFactory
in ExecutionContext
produced daemon threads program may exit without waiting on Futures to finish.
Now in the following scenario:
val x: Future[MyType] = finishInSomeFuture()
Assuming that x has a timeout that I can call like this:
Future { blocking { x.get(3, TimeOut.SECONDS) } }
Am I really blocking? Is there a better way to timeout asynchronously?
You probably meant Await.result
instead of x.get
:
def inefficientTimeoutFuture[T](f:Future[T], x:Duration) = Future { Await.result(f, x) }
In this case future f
will be calculate in separate thread, while additional thread will be blocked waiting for the calculation of f
.
Using scheduler to create TimeoutFuture is more efficient, as schedulers usually share fixed amount of threads (often one), while blocking in Await.result
will always require additional thread to block.
I would like to know how I could timeout without blocking?
Using scheduler to create TimeoutFuture allows you to timeout operation without blocking. You are wrapping your Future in timeout helper, and the new Future either completes successfully or fails due to timeout (whatever comes first). The new Future has the same asynchronous nature and it's up to you how to use it (register onComplete callbacks or synchronously wait for result, blocking main thread).
UPD I'll try to clarify some fundamental things about multithreading and blocking.
Right now asynchronous non-blocking approach is the trend, but you have to understand what blocking means and why it should be avoided.
Each thread in Java comes at the cost. First, it's relatively expensive to create new Thread (that's why thread pools exist) and second, it consumes memory. Why not CPU? Because your CPU resources are limited by the number of cores you have. It doesn't matter how many active threads you have, your parallelism level will always be capped by number of cores. And if thread is inactive (blocked) it doesn't consume CPU.
In contemporary java applications you can create fairly large number of threads (thousands of them). The problem is that in some cases you can't predict how many threads you're gonna need. That's when asynchronous approach comes into play. It says: instead of blocking current thread while some other thread(s) do their job let's wrap our next steps in callback and return current thread to the pool, so it can do some other useful work. So almost all threads are busy doing actual work instead of just waiting and consuming memory.
Now to the example of the timer. If you use netty-based HashedWheelTimer
you can have it backed by single thread and have thousands of events scheduled. When you create Future
that is blocked waiting for timeout you occupy one thread per "schedule". So if you have thousand timeouts scheduled, you'll end up with thousand blocked thread (which again consume memory, not cpu).
Now your "main" future (that you want to wrap in timeout) doesn't have to block the thread either. For example, if you perform synchronous http request inside the future, your thread will be blocked, but if you use netty-based AsyncHttpClient
(for example), you can use a promise-based future that doesn't occupy the thread. And in this case you can have small fixed number of threads that process any number of requests (hundreds of thousands).
UPD2
But there should be some thread that should be blocking even in case of the Timer as it has to wait for the Timeout millis. So what and where is the benefit? I still block, but may be I block less in the Timer case or?
This is true only for one particular scenario: when you have main thread that waits for asynchronous task to complete. In this case you're right, there is no way to wrap operation in timeout without blocking main thread. And it doesn't make any sense to use Timers in this case. You just need additional thread to perform your operation, while main thread waits for result or timeout.
But usually Futures
are used in more complex scenarios, where there is no "main" thread. For example, imagine asynchronous webserver, request comes in, you create Future to process it and register callback to reply. No "main" thread to wait for anything.
Or another example, you want to make 1000 requests to external service with individual timeouts and then gather all results in one place. If you have asynchronous client for that service, you create that 1000 requests, wrap them in asynchronous timeouts and then combine into one Future. You can block main thread to wait for that future to complete or register callback to print result, but you don't have to create 1000 threads just to wait for each individual request to complete.
So, the point is: if you already have synchronous flow and you want to wrap some part of it in timeout, the only thing you can do is to have your current thread blocked until other thread(s) perform the job. If you want to avoid blocking, you need to use asynchronous approach from the start.
Upvotes: 7