I do:

Task.Factory.StartNew(() =>Method(),ct) 

Elsewhere I call tokenSource.Cancel(); but nothing happens.

It seemed to me that such an overload should completely remove the task.

It turns out that you need to transfer the token directly to the method itself and perform analysis there?

Is it possible to somehow cancel the task, without passing the token inside?

    2 answers 2

    See it.

    You cannot “cancel” an already running code — for the same reasons that you cannot “kill” a running thread. Therefore, the code run via Task.Run will run to the end, unless he himself analyzes the token and cancels himself.

    However, the task will not be started if the token is already in the canceled state. Since launching Task 'a is a consumable thing, this is a good optimization.

    In addition, the task will be associated with this token, and if the code in Method throws an exception through token.ThrowIfCancellationRequested , this exception will be considered related to the task. Thus, the task will be terminated in the Cancelled state, and not Faulted .

    Source of information: https://social.msdn.microsoft.com/Forums/en-US/c2f614f6-c96c-4821-84cc-050b21aaee45/taskfactorystartnew-cancellation-token-parameter?forum=parallelextensions


    Reproducing example:

     async Task Run() { var cts = new CancellationTokenSource(); var ct = cts.Token; var t = Task.Run((Action)(() => // каст нужен! пояснение в конце ответа { while (true) { ct.ThrowIfCancellationRequested(); Thread.Sleep(200); } }), cts.Token); await Task.Delay(400); cts.Cancel(); try { await t; } catch (TaskCanceledException ex) { Console.WriteLine($"caught TaskCanceledException, task status = {t.Status}"); } catch (OperationCanceledException ex) { Console.WriteLine($"caught OperationCanceledException, task status = {t.Status}"); } } 

    This code falls into catch (OperationCanceledException ex) , and the status of the task is Canceled . And if you remove the token, then we also fall into catch (OperationCanceledException ex) , but the status is Faulted . In this case, runtime thinks that it was not the cancellation of the task that occurred, but simply some other extraneous exception.

    (We get to TaskCanceledException if the task has been canceled before launch.)


    (An error in the previous version of the code was that there was no explicit cast to Action [which is almost never needed!], And because of the infinite loop inside, the type driver could not decide whether it was overloading with Action or overloading with Func<Task> ! As a result he chose not what was needed, and rantaym believed that it was not the task that was canceled, but the process of its creation.)

    • The difference is due to the fact that when the token is transferred to the task, it immediately goes to the Canceled state, without waiting for the code to complete. This can be noticed by rearranging the exception and the delay - or by looking at the glassrays. - Pavel Mayorov
    • @PavelMayorov: Ugh, really, the task doesn't have time to start! Thank! - VladD
    • @PavelMayorov: Hm, I still can’t bring it to Faulted state. Strange. - VladD
    • ugh, thanks, and true - if the task managed to start, then it will wait for the end ... - Pavel Mayorov
    • OperationCanceledException perceived as a cancellation of the task. Nothing strange. - Pavel Mayorov

    Yes, the token must be passed to the method, and there periodically call ThrowIfCancellationRequested() . However, this does not negate the need to pass it by the second parameter StartNew() . This is done in case the cancellation of the operation is requested before your method starts to be executed (as well as in order to associate the task with the token). Like that:

     var cts = new CancellationTokenSource(); Task.Factory.StartNew(() => { while (true) { cts.Token.ThrowIfCancellationRequested(); Console.WriteLine("I'm working..."); } }, cts.Token); cts.CancelAfter(10); 

    If cancellation was requested, the ThrowIfCancellationRequested() method ThrowIfCancellationRequested() throw an OperationCanceledException . If you wish, you can catch it and inform the user that the operation was successfully canceled, for example.

    If you want, then you can do without an exception, but simply in the method to check:

     if (token.IsCancellationRequested) { /*выходим из метода*/ } 

    But then the task itself will not know that it was canceled. In the Status property, she will have TaskStatus.RanToCompletion instead of TaskStatus.Canceled .

    UPD: Additional material on the topic: https://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx