9

.NET 4.5.1: It appears, I can't cancel a blocking method running inside a task using the CancellationTokenSource built-in timeout.

class Program
{
    static void Main(string[] args)
    {
        var cts = new System.Threading.CancellationTokenSource();

        System.Console.CancelKeyPress += (s, e) =>
        {
            e.Cancel = true;
            cts.Cancel();
        };

        MainAsync(args, cts.Token).Wait();
    }

    // MainAsync from http://stackoverflow.com/questions/9208921/async-on-main-method-of-console-app
    static async Task MainAsync(string[] args, System.Threading.CancellationToken token)
    {
        Console.WriteLine("Starting MainAsync");
        var cts = new System.Threading.CancellationTokenSource(3000);
        var task = Task.Factory.StartNew(() =>
        {
            Console.WriteLine("Starting task...");
            var t = new System.Net.Sockets.TcpClient();
            var buffer = new byte[t.ReceiveBufferSize];
            t.Connect(new System.Net.IPEndPoint(System.Net.IPAddress.Parse("127.0.0.1"), 1337));

            Console.WriteLine("Recieving...");
            t.Client.Receive(buffer);
            Console.WriteLine("Finished Recieving...");

            return true;
        }, cts.Token);

        var success = await task;

        Console.WriteLine("Did the task complete succesfuly?", success);
    }
}

The output from the above Short, Self Contained, Correct Example (I hope it's correct) is:

Starting MainAsync
Starting task...
Recieving...

Why does the task doesn't cancel, no exception is thrown?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
wildeyes
  • 6,767
  • 5
  • 19
  • 37
  • Using socket.receive as you are using here is a blocking call -- you should provide a timeout or async usage so that you can re-evaluate the cancellationtoken during processing. CancellationToken won't magically terminate blocking calls for you. – Joe Nov 22 '15 at 14:10

2 Answers2

6

As I state on my blog, "You keep using that CancellationToken there. I do not think it means what you think it means."

In particular, the CancellationToken passed to StartNew will only cancel the starting of the delegate. If you want the delegate itself to support cancellation, then the delegate itself will have to observe the CancellationToken.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • How can a `Task.Run` be cancelled if it's wrapping a synchronous-blocking method without resorting to killing the thread it's running on? – Dai Aug 28 '18 at 23:23
  • @Dai: If the method doesn't support cancellation, then termination is your only option. Thread termination is unsafe; for maximum reliability, you should terminate the process. – Stephen Cleary Aug 29 '18 at 01:14
  • My apologies, I meant that I wanted to cancel the wait, like in this thread: https://stackoverflow.com/questions/14524209/what-is-the-correct-way-to-cancel-an-async-operation-that-doesnt-accept-a-cance/14524565#14524565 - can you weigh-in on if @i3arnon's answer is better than @casperOne's? – Dai Aug 29 '18 at 04:12
  • @Dai: Either of them look fine to me. I [tend to use](https://github.com/StephenCleary/AsyncEx/blob/c01321ddda71d7fac302fc95653fd6694af433cf/src/Nito.AsyncEx.Tasks/TaskExtensions.cs#L19) the accepted answer because it's less clever. – Stephen Cleary Aug 29 '18 at 13:48
3

I am not sure but I guess you are confusing "requesting a cancellation" with "terminating or aborting a thread/task". These are two completly different things. According to the description about the Canellation in Managerd Threads the provided functionality enables you to send something like a signal, indicating, that the operation in progress shall be stopped.

How and if you react on that signal is - as a programmer - up to you.

In your example you've started a new task

var task = Task.Factory.StartNew(() =>
{      
    Console.WriteLine("Starting task...");
    var t = new System.Net.Sockets.TcpClient();
    var buffer = new byte[t.ReceiveBufferSize];
    t.Connect(new System.Net.IPEndPoint(System.Net.IPAddress.Parse("127.0.0.1"), 1337));

    Console.WriteLine("Recieving...");
    t.Client.Receive(buffer);
    Console.WriteLine("Finished Recieving...");

    return true;
}, cts.Token);

which does not handle the cancellation nor is it suitable to do so. Canellation would be used e.g. if you want to break out of a loop with many iterations - therefore you would check in each iteration whether the CancellationToken.IsCancellationRequested has been set to true or not. If so, you can react accordingly.

What you want is to abort the thread that's behind the current task which is in my opinion only possible by creating a new instance of the Thread class for yourself and handle the abort accordingly.

Community
  • 1
  • 1
Markus Safar
  • 6,324
  • 5
  • 28
  • 44