繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM