Cooperative cancellation in F# with cancel continuation

自古美人都是妖i 提交于 2021-01-27 17:50:31

问题


Probably I have here 2 questions instead of one, but anyway.

I'm implementing cooperative cancellation as here suggested. Here is my test code:

type Async with
    static member Isolate(f : CancellationToken -> Async<'T>) : Async<'T> =
        async {
            let! ct = Async.CancellationToken
            let isolatedTask = Async.StartAsTask(f ct)
            return! Async.AwaitTask isolatedTask
        }

let testLoop (ct: CancellationToken) = async {
    let rec next ix =
        if ct.IsCancellationRequested then ()
        else 
            printf "%i.." ix
            Thread.Sleep 10  
            next (ix+1)
    next 1
}

let cancellationSource = new CancellationTokenSource()
let onDone () = printfn "!! DONE"
let onError _ = printfn "!! ERROR"
let onCancel _ = printfn "!! CANCEL"

Async.StartWithContinuations (Async.Isolate testLoop, onDone, onError, onCancel, cancellationSource.Token)

Thread.Sleep(100)
cancellationSource.Cancel ()
Thread.Sleep(500)

As you can see, I start async with done, cancel and error continuations. If I run that code as is, I'll get the following output:

1..2..3..4..5..6..7..8..!! DONE

If I slightly update the Isolate method as follows:

    static member Isolate(f : CancellationToken -> Async<'T>) : Async<'T> =
        async {
            let! ct = Async.CancellationToken
            let isolatedTask = Async.StartAsTask(f ct)
            let! x = Async.AwaitTask isolatedTask
            x
        }

I get the expected (by myself) output:

1..2..3..4..5..6..7..!! CANCEL

Why do we have such difference in the behavior?

Is it possible to abort the testLoop, if it does not cancelled within some timeout?


回答1:


The async block checks for cancellation of the Async.CancellationToken only before and after bind (written using let!). This means that when the token gets cancelled, the workflow will only get cancelled when there is more work to be done.

It is also worth noting that isolatedTask does not itself gets cancelled in this example, because it just terminates regularly (using if).

In your case:

  • When you use just return!, the task returns regularly, Async.AwaitTask returns regularly and nothing is done afterwards so the workflow completes.

  • When you use let! followed by return, the task returns regularly and Async.AwaitTask returns regularly, but then let! checks for cancellation before running return and this cancels the workflow.



来源:https://stackoverflow.com/questions/37596011/cooperative-cancellation-in-f-with-cancel-continuation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!