问题
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 byreturn
, the task returns regularly andAsync.AwaitTask
returns regularly, but thenlet!
checks for cancellation before runningreturn
and this cancels the workflow.
来源:https://stackoverflow.com/questions/37596011/cooperative-cancellation-in-f-with-cancel-continuation