首页 > 解决方案 > 在 F# 中运行超时的 IO 任务

问题描述

我正在尝试与 F# 中的云服务交谈,并在这些操作周围出现超时。

现在这是多次迭代,但我仍然无法弄清楚如何使用 Async。

    let tokenSource = new CancellationTokenSource()
    let task        = Async.StartAsTask(async {return (getS3Meta keyMeta)}, TaskCreationOptions.None, tokenSource.Token)
    let metaMaybe   = Async.AwaitTask(task, 700) |> Async.RunSynchronously

为了支持我们引入的超时:

  type Timeout = Timeout

  type Microsoft.FSharp.Control.Async with

    static member AwaitTask (t : Task<'T>, timeout : int) =
        async {
            use cts = new CancellationTokenSource()
            use timer = Task.Delay (timeout, cts.Token)
            let! completed = Async.AwaitTask <| Task.WhenAny(t, timer)
            if completed = (t :> Task) then
                cts.Cancel ()
                let! result = Async.AwaitTask t
                return Ok result
            else return Error Timeout
        }

无论我做什么,任务总是在 WaitingForActivation 中。

IO操作超时的推荐方式是什么,它可以阻止主执行,我这里不需要异步代码。如果我必须使用异步,那么用于启动超时任务的原语是什么?

标签: asynchronousf#

解决方案


当前工作的代码(即使我们不了解与不同异步工作流相关的不同权衡,我们也没有遇到问题)。

  let doSomeIoAsync =
    async {
      return (anotherAsyncWokFlowIoInCsharpLib someParam)
    }

  let task = Async.StartAsTask doSomeIoAsync

  match task.Wait(500) with
   | true ->  Success task.Result
   | false -> Error Timeout

一些fsi:

> let ioFn () = Thread.Sleep(5000); System.Console.WriteLine 100; 100;;
Real: 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
val ioFn : unit -> int

> ioFn();;
100
Real: 00:00:05.005, CPU: 00:00:00.001, GC gen0: 0, gen1: 0, gen2: 0
val it : int = 100

> let taskAsync = async { return ioFn() };;
Real: 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
val taskAsync : Async<int>

> let t = Async.StartAsTask taskAsync ;;
Real: 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
val t : Tasks.Task<int>

> t.Wait(500) ; t.Status;;
Real: 00:00:00.505, CPU: 00:00:00.001, GC gen0: 0, gen1: 0, gen2: 0
val it : Tasks.TaskStatus = WaitingForActivation

> 100

- t.Wait(500) ; t.Status;;
Real: 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
val it : Tasks.TaskStatus = RanToCompletion

> t.Result;;
Real: 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
val it : int = 100

我认为这样使用 WaitingForActivation 有点误导。也许我误解了它,它真的是 WaitingForCompletion。

就性能而言,前几个请求与其余请求相比非常慢:

Error: WaitingForActivation
Elapsed Time: 296
Error: WaitingForActivation
Elapsed Time: 254
Error: WaitingForActivation
Elapsed Time: 255
Error: WaitingForActivation
Elapsed Time: 254
Error: WaitingForActivation
Elapsed Time: 254
OK: RanToCompletion
Elapsed Time: 249
OK: RanToCompletion
Elapsed Time: 147
OK: RanToCompletion
Elapsed Time: 246
OK: RanToCompletion
Elapsed Time: 146
OK: RanToCompletion
Elapsed Time: 150
OK: RanToCompletion
Elapsed Time: 142
OK: RanToCompletion
Elapsed Time: 143
OK: RanToCompletion
Elapsed Time: 139
OK: RanToCompletion
Elapsed Time: 139
OK: RanToCompletion
Elapsed Time: 141

不知道如何摆脱缓慢的启动。


推荐阅读