如何在F#中创建同步函数的异步版本?

时间:2011-12-22 14:13:02

标签: asynchronous f# functional-programming

您可以使用哪些不同的技术来创建Async的实例<'T>在F#?

我看到Web客户端/请求和文件流有很多扩展方法,但如果我想编写自己的异步计算提供程序,我将如何编写同步的AsyncDoSomething版本DoSomething函数?

我知道您可以使用相同签名的委托来包装原始函数,然后在Async.FromBeginEndBeginInvoke方法上使用EndInvoke

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

但这感觉有点强迫,它似乎不是'F#方式',因为你必须使用在C#或VB中定义的委托(其中有很多System.Action和{{1} }变体可供选择当然)因为F#代理不支持System.FuncBeginInvoke方法。

有没有人列出了在F#中编写异步版同步函数的不同方法?

非常感谢提前!

3 个答案:

答案 0 :(得分:6)

来自docs for Async,所有AwaitXXXFromXXX方法。但最常见的方法是使用asynchronous workflows。但是,正如Mauricio评论的那样,用async { }包装任意代码并不总是有益的。

更新

这里有一些代码来证明这一点。

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

Stream.Read包装同步async { }不会产生任何可观察到的好处。异步工作流主要是异步操作的便捷方式。也就是说,它取决于具有良好编写的异步操作,以作为构建块。

答案 1 :(得分:3)

你可以从Async.FromContinuations获得一些好的里程。该函数允许您从延续函数定义Async。如果你看一下WebClient的AsyncDownloadString的定义,你会看到它正在使用中。

答案 2 :(得分:3)

你可以这样写:

let doAsyncTask  (f : unit->'a) = 
     async { return! Task<'a>.Factory.StartNew( new Func<'a>(f) ) |> Async.AwaitTask }

并像

一样使用它
let asyncFunc arg = async { return! doAsyncTask( fun () -> syncFunc arg ) }