我真的很喜欢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
你可以看到,它按预期工作。它非常好,但它也有点尴尬,我不确定它的安全性和可能出现的其他问题。也许我正在重新发明轮子,并且有很好的简洁方法来编写这样的工作流程?
答案 0 :(得分:7)
UPD:目前Vesa A.J.K提出了最佳选择: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
。
答案 1 :(得分:4)
请参阅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)
this的实施方式类似:
{{1}}
我不相信它会在第一个计算结束时取消其他计算,但至少这个实现不会不必要地阻塞一个线程。
答案 2 :(得分:4)
只需使用Async.StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
:
let with_timeout timeout action =
async {
let! child = Async.StartChild( action, timeout )
return! child
}