asynchronous - 有什么原因不能立即取消 Async.Sleep 吗?

标签 asynchronous f#

下面的测试表明 F# 2.0 中的 Async.Sleep 不能立即取消。只有在时间过去后,我们才会收到“取消”通知。

module async_sleep_test
    open System
    open System.Threading
    open System.Threading.Tasks
    open System.Xml

    let cts = new CancellationTokenSource()
    Task.Factory.StartNew(fun () -> 
        try
            Async.RunSynchronously(async{
                printfn "going to sleep"
                do! Async.Sleep(10000)
            }, -1(*no timeout*), cts.Token)
            printfn "sleep completed"
        with 
        | :? OperationCanceledException ->
            printfn "sleep aborted" // we will see it only after 10 sec.
        | _ ->
            printfn "sleep raised error"
    ) |> ignore
    Thread.Sleep(100) // give time to the task to enter in sleep
    cts.Cancel()
    Thread.Sleep(100) // give chance to the task to complete before print bye message
    printfn "press any key to exit...."
    Console.ReadKey(true) |> ignore

我认为这是不正确的行为。你怎么认为这是一个错误?例如,如果我将使用以下实现,会不会有任何意外:
static member SleepEx(milliseconds:int) = async{
    let disp = new SerialDisposable()
    use! ch = Async.OnCancel(fun()->disp.Dispose())
    do! Async.FromContinuations(fun (success, error, cancel) ->
        let timerSubscription = new SerialDisposable()
        let CompleteWith = 
            let completed = ref 0
            fun cont ->
                if Interlocked.Exchange(completed, 1) = 0 then
                    timerSubscription.Dispose()
                    try cont() with _->()

        disp.Disposable <- Disposable.Create(fun()->
            CompleteWith (fun ()-> cancel(new OperationCanceledException()))
        )
        let tmr = new Timer(
            callback = (fun state -> CompleteWith(success)), 
            state = null, dueTime = milliseconds, period = Timeout.Infinite
        )
        if tmr = null then
            CompleteWith(fun ()->error(new Exception("failed to create timer")))
        else
            timerSubscription.Disposable <- Disposable.Create(fun()->
                try tmr.Dispose() with _ -> ()
            )
    )
}

最佳答案

我不会说这是一个错误 - 它遵循一般在 F# 异步工作流中处理取消的方式。通常,F# 假定您使用 let! 调用的原始操作或 do!不支持取消(我猜在 .NET 中没有标准机制),因此 F# 在使用 let! 进行的调用之前和之后插入取消检查。 .

所以打个电话let! res = foo()实际上更像是以下内容(尽管检查隐藏在 async 的库实现中):

token.ThrowIfCancellationRequested()
let! res = foo()
token.ThrowIfCancellationRequested()

当然是foo()返回的工作流可以更好地处理取消 - 通常,如果它是使用 async { .. } 实现的块,那么它将在每个 let! 周围包含更多检查.但是,一般情况下(除非某些操作以更聪明的方式实现),取消会在下一个 let! 之后执行。调用完成。

您对 Sleep 的替代定义对我来说看起来不错 - 它比 F# 库中可用的更好地支持取消,如果您需要立即取消,然后替换 F# 的 Async.Sleep与您的 SleepEx是唯一的出路。但是,可能仍然会有一些不支持立即取消的操作,因此您可能会在其他地方遇到问题(如果您到处都需要这种行为)。

PS:我想你的SleepEx功能可能对其他人非常有用。如果您可以在 F# Snippets web site 上分享它,那太棒了!

关于asynchronous - 有什么原因不能立即取消 Async.Sleep 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9041491/

相关文章:

javascript - 当所有数据加载时如何运行函数?

string - 在 F# 中查找字符串序列中所有出现的字符串?

f# - 为什么在这种情况下 F# 不能推断类型?

Haskell 组合 (.) 与 F# 的管道前向运算符 (|>)

amazon-web-services - 如何使用 AWS ApiGateway HttpApi 调用 lambda 异步

javascript - NodeJS 运行系列 - 它实际上是做什么的?

performance - Ping 功能使整个 Excel 表变慢/无响应

testing - ember-simple-auth,验收测试和等待异步操作

f# - F# 中自定义运算符的优先级

f# - 与类型注释的模式匹配