Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create async version of a synchronous function in F#?

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!

like image 852
theburningmonk Avatar asked Dec 22 '11 14:12

theburningmonk


People also ask

How do you make a synchronous call asynchronous?

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.

Is async function synchronous?

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.

Can async function be called from Sync function?

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 .

Does async await make it synchronous?

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.


1 Answers

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.

UPDATE

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.

like image 107
Daniel Avatar answered Sep 18 '22 06:09

Daniel