簡體   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