Home > Blockchain >  Why does this synchronously-run async sleep hang?
Why does this synchronously-run async sleep hang?

Time:10-03

Consider this code:

open System
open System.Threading

ThreadPool.SetMinThreads (1, 1) |> ignore
ThreadPool.SetMaxThreads (1, 1) |> ignore

let asyncOp i = async {
    printfn "started %i" i
    do! Async.Sleep 1000
    printfn "finished %i" i }

for i = 1 to 3 do Async.Start <| asyncOp i

printfn "read"
Console.ReadLine () |> ignore

I understand, to paraphrase the documentation for SetMaxThreads, that just one request to the thread pool can be active concurrently, and that other requests will remain queued until the thread becomes available. It prints

read
started 1
started 2
started 3
finished 3   // after a second
finished 2
finished 1

Incidentally, why do the "finished" lines print in reverse order?

Now change do! to Async.RunSynchronously <|. This time we get only

read
started 1

Why does it stop there? I would have expected the same as what you get when you replace the whole line with Thread.Sleep 1000:

read
started 1
finished 1   // after a second
started 2
finished 2   // after a second
started 3
finished 3   // after a second

CodePudding user response:

Here is what the documentation says about Async.RunSynchronously:

When to use it:

If you need it, use it only once in an application - at the entry point for an executable. When you don't care about performance and want to execute a set of other asynchronous operations at once.

What to watch out for:

Calling Async.RunSynchronously blocks the calling thread until the execution completes.

CodePudding user response:

Note that after switching to Async.RunSynchronously, the code hangs even when running just one operation.

The ThreadPool.SetMaxThreads docs state:

You cannot set the maximum number of worker threads or I/O completion threads to a number smaller than the number of processors on the computer.

Apparently that's not quite true. You can set the value lower (no exception is thrown) but perhaps it means to suggest that this a very bad idea. However, I have noticed that even after upping the thread counts to 12 or 13 the code still hangs when running 100 async operations. Going up to 24 threads allows it to complete.

For me, ThreadPool.GetMaxThreads() returns (32767, 1000) by default. This suggests to me that the .NET threading system really isn't designed to work with such low numbers of threads and that you shouldn't be reducing them to anywhere near 1.

The SetMaxThreads docs also say:

Use caution when changing the maximum number of threads in the thread pool. While your code might benefit, the changes might have an adverse effect on code libraries you use.

If you're trying to achieve a particular behaviour by changing the ThreadPool max threads then I would suggest exploring a different approach.

  • Related