I have a small program using the producer/consumer pattern in F#. I want to start 1 producer, then 8 consumers, wait for the producer then wait for the consumers and end the program.
The code that sets this in motion is:
async {
let! p = Async.StartChild producer
let maxConcurrent = 8
let consumers = List<Async<unit>>()
for _ in 0 .. maxConcurrent - 1 do
let! c = Async.StartChild consumer
consumers.Add(c)
do! p
for i in 0 .. maxConcurrent - 1 do
do! consumers[i]
}
|> Async.RunSynchronously
While this works it feels wrong to use the mutable List from System.Collections.Generic instead of just F# list. But I cannot generate the list of Async needed using List. I have tried:
let consumers2 = List.init 8 (fun _ -> async {
let! c = Async.StartChild consumer
return c
})
But this now wraps it in async again, so it becomes a list<async<async<unit>>>
, if it's instead return! then the app hangs (I'm guessing because I'm not awaiting the task that should start the Task instead awaiting also the task that awaits finalization)
CodePudding user response:
I think Async.Parallel
will flatten the asyncs the way you want. The following seems to work correctly for me:
async {
// I need the producer to run in it's own thread, not shared by the threadpool, because I rely on the consumer being allowed to block the thread with .Take
let! p = Async.StartChild producer
let maxConcurrent = 8
let! consumers =
List.init maxConcurrent (fun _ ->
Async.StartChild consumer)
|> Async.Parallel
do! p
for c in consumers do
do! c
}
|> Async.RunSynchronously