What are the different techniques you can use to create instances of Async<'T> in F#?
I see there are a number of extension methods for web client/request and file stream, but if I want to write my own provider of async computations, how would I go about writing those AsyncDoSomething
versions of my synchronous DoSomething
functions?
I know that you can use a delegate of the same signature to wrap the original function, and then use Async.FromBeginEnd
on the BeginInvoke
and EndInvoke
methods:
open System
let del : Func<int> = new Func<int>(fun () -> 42)
let delAsync = async {
let! res = Async.FromBeginEnd(del.BeginInvoke, del.EndInvoke)
printfn "result was %d" res
}
Async.Start delAsync
But this feels a little forced and it doesn't seem to be the 'F# way' as you have to use delegates defined in C# or VB (of which there are plenty of System.Action
and System.Func
variants to choose from of course) because F# delegates don't support the BeginInvoke
and EndInvoke
methods.
Does anyone have a list of the different ways you can write an async version of a synchronous function in F#?
Many thanks in advance!
The simplest way to execute a method asynchronously is to start executing the method by calling the delegate's BeginInvoke method, do some work on the main thread, and then call the delegate's EndInvoke method. EndInvoke might block the calling thread because it does not return until the asynchronous call completes.
Sync is single-thread, so only one operation or program will run at a time. Async is non-blocking, which means it will send multiple requests to a server. Sync is blocking — it will only send the server one request at a time and will wait for that request to be answered by the server.
A synchronous method calls an async method, obtaining a Task . The synchronous method does a blocking wait on the Task . The async method uses await without ConfigureAwait .
An async function runs synchronously until the first await keyword. This means that within an async function body, all synchronous code before the first await keyword executes immediately.
From the docs for Async
, all the AwaitXXX
and FromXXX
methods. But the most common way is using asynchronous workflows. However, as Mauricio commented, wrapping arbitrary code with async { }
isn't always beneficial.
Here's a bit of code to demonstrate that point.
open System.IO
let BUF_SIZE = 1 <<< 16 //64KB
let readFile f (stream:Stream) =
let buf = Array.zeroCreate BUF_SIZE
let rec read p =
async {
let! n = f stream buf
match n with
| 0 -> ()
| _ -> return! read (p + n)
}
read 0
let fakeAsyncReadFile s = readFile (fun stream buf ->
async { return stream.Read(buf, 0, buf.Length) }) s
let realAsyncReadFile s = readFile (fun stream buf ->
stream.AsyncRead(buf, 0, buf.Length)) s
let files = [@"C:\big_file_1"; @"C:\big_file_2"]
let readWith f =
let streams = Seq.map File.OpenRead files
try Seq.map f streams |> Async.Parallel |> Async.RunSynchronously |> ignore
finally streams |> Seq.iter (fun s -> s.Close())
readWith fakeAsyncReadFile //Real: 00:00:34.190, CPU: 00:00:03.166, GC gen0: 4, gen1: 2, gen2: 1
readWith realAsyncReadFile //Real: 00:00:05.101, CPU: 00:00:16.957, GC gen0: 31, gen1: 1, gen2: 0
Wrapping the synchronous Stream.Read
with async { }
yields no observable benefits. An async workflow is primarily a convenient way to chain asynchronous operations. That is, it depends on having well-written asynchronous operations to start with, to serve as building blocks.
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