具有超时的F#异步工作流程

时间:2014-03-07 08:41:13

标签: .net asynchronous f# 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    

你可以看到,它按预期工作。它非常好,但它也有点尴尬,我不确定它的安全性和可能出现的其他问题。也许我正在重新发明轮子,并且有很好的简洁方法来编写这样的工作流程?

3 个答案:

答案 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类似。

通过使用它,您可以实现Tasklet 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
  }