问题
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.AwaitTaskreturns regularly and nothing is done afterwards so the workflow completes.When you use
let!followed byreturn, the task returns regularly andAsync.AwaitTaskreturns regularly, but thenlet!checks for cancellation before runningreturnand this cancels the workflow.
来源:https://stackoverflow.com/questions/37596011/cooperative-cancellation-in-f-with-cancel-continuation