[英]F# async workflow with timeout
我真的很喜歡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.WhenAny
這個實現,它應該與Task.WhenAny
類似。
通過使用它,您可以實現withTimeout
類似於在此處為Task
實現的方式:
let withTimeout dueTime comp =
let success = async {
let! x = comp
return (Some x)
}
let timeout = async {
do! Async.Delay(dueTime)
return None
}
Async.WhenAny(success, timeout)
我不相信它會在第一個計算結束時取消其他計算,但至少這個實現不會不必要地阻塞一個線程。
只需使用Async.StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
:
let with_timeout timeout action =
async {
let! child = Async.StartChild( action, timeout )
return! child
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.