Closing Channels in F#
open System
// In this example we'll use a `jobs` channel to communicate work to be done
// from the main function to a worker function. When we have no more jobs for
// the worker we'll close the `jobs` channel.
let main() =
let jobs = new System.Collections.Concurrent.BlockingCollection<int>(5)
let done = new System.Threading.ManualResetEvent(false)
// Here's the worker function. It repeatedly receives from `jobs`.
// We use TryTake to check if the collection is completed and all
// values have been received. We use this to notify on `done` when
// we've worked all our jobs.
let worker() =
let mutable more = true
while more do
match jobs.TryTake() with
| true, j ->
printfn "received job %d" j
| false, _ ->
printfn "received all jobs"
done.Set() |> ignore
more <- false
// Start the worker
Async.Start(worker)
// This sends 3 jobs to the worker over the `jobs` channel, then closes it.
for j in 1..3 do
jobs.Add(j)
printfn "sent job %d" j
jobs.CompleteAdding()
printfn "sent all jobs"
// We await the worker using the synchronization approach
done.WaitOne() |> ignore
// Checking if the channel is closed
let moreJobs = not jobs.IsCompleted
printfn "received more jobs: %b" moreJobs
main()
This F# code demonstrates the concept of closing channels using BlockingCollection<T>
, which is similar to Go’s channels. Here’s a breakdown of the translation:
We use
BlockingCollection<int>
to represent thejobs
channel. This collection allows adding and taking items in a thread-safe manner.Instead of Go’s
done
channel, we use aManualResetEvent
for synchronization.The worker function is defined as
worker()
. It usesTryTake()
to attempt to retrieve jobs from the collection. IfTryTake()
returnsfalse
, it means the collection is completed and empty.We start the worker as an asynchronous operation using
Async.Start(worker)
.Jobs are added to the collection using
jobs.Add(j)
.After sending all jobs, we call
jobs.CompleteAdding()
to indicate that no more items will be added. This is equivalent to closing the channel in Go.We wait for the worker to finish using
done.WaitOne()
.Finally, we check if the collection is completed using
jobs.IsCompleted
, which is similar to checking if a channel is closed in Go.
To run this program, save it as ClosingChannels.fs
and use the F# compiler:
$ fsharpc ClosingChannels.fs
$ mono ClosingChannels.exe
This example demonstrates how to use BlockingCollection<T>
in F# to achieve similar behavior to Go’s channel closing. The concept of closed channels leads naturally to iterating over collections, which we’ll explore in the next example.