Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# - cancellation token works for async{} but not for task{}

Tags:

task

f#

f#-async

When I run this code-block using the async{} computation expression:

let tokenSource = new CancellationTokenSource()

let runAsync() =
    async {
        while true do
            do! Async.Sleep(1000 * 1)
            printfn "hello"
    }

Async.Start(runAsync(), tokenSource.Token)

...and then run tokenSource.Cancel(), the executing process is cancelled, as expected.

However, when I run this extremely similar code-block using task{}:

let tokenSource = new CancellationTokenSource()

let rec runTask() =
    task {
        while true do
            do! Task.Delay(1000 * 1)
            printfn "hello"
    }
let run () = runTask () :> Task

Task.Run(run, tokenSource.Token)

...and then run tokenSource.Cancel(), the executing process is NOT cancelled.

Why does the cancellation token function as expected for async{} but not for task{}?

like image 496
Overlord Zurg Avatar asked Oct 24 '25 14:10

Overlord Zurg


1 Answers

That’s by design. For performance and other reasons, tasks are deliberately hot started, while Async is cold-started. Cold-started asynchronous operations can be given a cancellation token. After a task is started, which is immediate in the case of task, it cannot be given a CT anymore.

What you need is cancellableTask from the IcedTask library.

Note that it’s fine to have an Async inside a task CE. That nested Async can be cancelled just like any other Async (similarly, it’s fine to pass the token to Task.DeLay, which will work for this scenario).

Note also that Task.Run in your code is redundant and should typically not be used. Calling runTask() (which shouldn’t be rec btw) internally already calls Task.Run (or equivalent), so all you’re doing is wrapping it in another task. Since CTs are not automatically passed on to child tasks, the CT doesn’t have effect.

And one more thing, if you do need rec in your real world code, be advised that tasks (from task CE) are not tail recursive, while async is. This may change soon, as this change is strongly considered in the near future.

like image 178
Abel Avatar answered Oct 26 '25 18:10

Abel