wildeyes
wildeyes

Reputation: 7517

CancellationToken doesn't cancel blocking method

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?

I am using .NET Framework 4.5.1.

Upvotes: 9

Views: 2124

Answers (2)

Markus Safar
Markus Safar

Reputation: 6580

I am not sure but I guess you are confusing "requesting a cancellation" with "terminating or aborting a thread/task". These are two completely different things. According to the description about the Cancellation in Managed 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. Cancellation 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.

Upvotes: 3

Stephen Cleary
Stephen Cleary

Reputation: 456507

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.

Upvotes: 8

Related Questions