This is probably one of the most elementary things in F#, but I've just realized I've got no idea what's going on behind the sceens.
let testMe() =
async { printfn "before!"
do! myAsyncFunction() // Waits without blocking thread
printfn "after!" }
testMe |> Async.RunSynchronously
What's happening at do! myAsyncFunction()
? I'm aware of that it waits for myAsyncFunction
to finish, before moving on. But how can it do that, without blocking the thread?
My best guess is that everything after do! myAsyncFunction()
is passed along as a continuation, which gets executed on the same thread myAsyncFunction()
was scheduled on, once myAsyncFunction()
has finished executing.. but then again, that's just a guess.
We can start threads with the Task class and wait for the threads to finish with the Task. WaitAll() method in C#. In the above code, we waited for the completion of the thread1 and thread2 tasks inside the main thread with the Task. WaitAll() method in C#.
Important points of Java Thread Sleep It always suspends the execution of the current thread. The actual thread sleeps until it wakes up, and the execution time depends on the system timers and schedulers. The sleeping thread does not block the current thread.
Thread. sleep is bad! It blocks the current thread and renders it unusable for further work.
Sleep method causes the current thread to immediately block for the number of milliseconds or the time interval you pass to the method, and yields the remainder of its time slice to another thread. Once that interval elapses, the sleeping thread resumes execution. One thread cannot call Thread. Sleep on another thread.
As you correctly pointed out, the myAsyncFunction
is passed a continuation and it calls it to resume the rest of the asynchronous workflow when it completes.
You can understand it better by looking at the desugared version of the code:
let testMe() =
async.Delay(fun () ->
printfn "before!"
async.Bind(myAsyncFunction(), fun () ->
printfn "after!"
async.Zero()))
They key thing is that the asynchronous workflow created by myAsyncFunction
is given to the Bind
operation that starts it and gives it the second argument (a continuation) as a function to call when the workflow completes. If you simplify a lot, then an asynchronous workflow could be defined like this:
type MyAsync<'T> = (('T -> unit) * (exn -> unit)) -> unit
So, an asynchronous workflow is just a function that takes some continuations as argument. When it gets the continuations, it does something (i.e. create a timer or start I/O) and then it eventually calls these continuations. The question "On which thread are the continuations called?" is an interesting one - in a simple model, it depends on the MyAsync
that you're starting - it may decide to run them anywhere it wants (i.e. Async.SwithcToNewThread
runs them on a new thread). The F# library includes some additional handling that makes GUI programming using workflows easier.
Your example uses Async.RunImmediate
, which blocks the current thread, but you could also use Async.Start
, which just starts the workflow and ignores the result when it is produced. The implementation of Async.Start
could look like this:
let Start (async:MyAsync<unit>) = async (ignore, ignore)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With