Как создать асинхронную версию синхронной функции в F #? - PullRequest
11 голосов
/ 22 декабря 2011

Какие методы вы можете использовать для создания экземпляров Async <'T> в F #?

Я вижу, что есть ряд методов расширения для веб-клиента / запроса и потока файлов, но если я хочу написать свой собственный поставщик асинхронных вычислений, как бы мне написать эти AsyncDoSomething версии моегосинхронные DoSomething функции?

Я знаю, что вы можете использовать делегата той же подписи для переноса исходной функции, а затем использовать Async.FromBeginEnd в методах BeginInvoke и 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 и System.Funcварианты на выбор, конечно), потому что делегаты F # не поддерживают методы BeginInvoke и EndInvoke.

У кого-нибудь есть список различных способов, которыми вы можете написать асинхронную версию синхронной функции вF #?

Большое спасибо заранее!

Ответы [ 3 ]

6 голосов
/ 22 декабря 2011

Из документов для Async, все методы AwaitXXX и FromXXX.Но наиболее распространенным способом является использование асинхронных рабочих процессов .Однако, как прокомментировал Маурисио, перенос произвольного кода на 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 { } не дает заметных преимуществ.Асинхронный рабочий процесс - это прежде всего удобный способ цепочки асинхронных операций.То есть это зависит от наличия хорошо написанных асинхронных операций, с которых можно начинать, чтобы они служили строительными блоками.

3 голосов
/ 24 ноября 2012

И вы можете написать так:

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 ) } 
3 голосов
/ 22 декабря 2011

Вы можете получить хороший пробег от Async.From Continuuations.Эта функция позволяет вам определять асинхронность из функций продолжения.Если вы посмотрите на определение AsyncDownloadString WebClient, вы увидите, что оно используется.

...