Dot-Net
帶有超時的 F# 非同步工作流
我真的很喜歡 F# 的
async工作流程,但對我來說,它有一個嚴重的問題:它不允許創建執行時間不超過某個特定時間跨度的工作流程。為了更清楚,這是我為自己編寫的一個簡單函式:
let withTimeout operation timeout = async { try return Some <| Async.RunSynchronously (operation, timeout) with :? TimeoutException -> return None }即簽名是
val withTimeout : operation:Async<'a> -> timeout:int -> Async<'a option>此處的範例用法:
let op = async { do! Async.Sleep(1000) return 1 } #time withTimeout op 2000 |> Async.RunSynchronously;; // Real: 00:00:01.116, CPU: 00:00:00.015, GC gen0: 0, gen1: 0, gen2: 0 // val it : unit option = Some 1 withTimeout op 2000 |> Async.RunSynchronously;; // Real: 00:00:01.004, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0 // val it : unit option = Some 1 withTimeout op 500 |> Async.RunSynchronously;; // Real: 00:00:00.569, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0 // val it : unit option = None您可以看到,它按預期工作。它非常好,但也有點尷尬,我不確定它的安全性和可能出現的其他問題。也許我正在重新發明輪子,並且有一種簡潔明了的方式來編寫這樣的工作流程?
UPD:Vesa AJK在這裡提出了目前的最佳選擇: https ://stackoverflow.com/a/26230245/1554463 。我的編輯是這樣的:
type Async with static member WithTimeout (timeout : int option) operation = match timeout with | Some time when time > 0 -> async { let! child = Async.StartChild (operation, time) try let! result = child return Some result with :? TimeoutException -> return None } | _ -> async { let! result = operation return Some result }這是另一種選擇:
type Async with static member WithCancellation (token:CancellationToken) operation = async { try let task = Async.StartAsTask (operation, cancellationToken = token) task.Wait () return Some task.Result with | :? TaskCanceledException -> return None | :? AggregateException -> return None } static member WithTimeout (timeout:int option) operation = match timeout with | Some(time) -> async { use tokenSource = new CancellationTokenSource (time) return! operation |> Async.WithCancellation tokenSource.Token } | _ -> async { let! res = operation return Some res }在這裡,我使用 .Net 任務和
CancellationToken.
只需使用
Async.StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>:let with_timeout timeout action = async { let! child = Async.StartChild( action, timeout ) return! child }