Why TaskCanceledException is thrown and does not always break in the debugger

I burst into the mechanism async-awaitand watched the casting TaskCanceledException, which I still can not explain.

In the example below (self-sufficient) I have a statement

await Task.Run(() => null);

I know that this statement in itself is useless, but I isolated the problem, the real code has logic and in some cases returns null.

Why is it throwing TaskCanceledException? If I return an arbitrary number (5 in the example below), it does not throw.

Also, if I am a awaitmethod, the VS debugger is broken, but if I am not await, then only the message is written to the VS output window.

internal class Program
{
    private static void Main(string[] args)
    {
        var testAsync = new TestAsync();

        // Exception thrown but the debugger does not step in. Only a message is logged to the output window
        testAsync.TestAsyncExceptionOnlyInTheOutputWindow();

        // Exception thrown and the debugger breaks
        testAsync.TestAsyncExceptionBreaksIntoTheDebugger();

        Console.ReadKey();
    }
}

internal class TestAsync
{
    public async void TestAsyncExceptionOnlyInTheOutputWindow()
    {
         TestNullCase();
    }

    public async void TestAsyncExceptionBreaksIntoTheDebugger()
    {
        await TestNullCase();
    }

    private static async Task TestNullCase()
    {
        // This does not throw a TaskCanceledException
        await Task.Run(() => 5);

        // This does throw a TaskCanceledException
        await Task.Run(() => null);
    }
} 
+4
3

TaskCanceledException

Task.Run(() => null) . static Task Run(Func<Task> function), static Task<TResult> Run<TResult>(Func<TResult> function), . , async, . , Task.Run "" (null) , , , .

, , ProcessInnerTask private UnwrapPromise<TResult> ( Task<TResult>):

private void ProcessInnerTask(Task task)
{
    // If the inner task is null, the proxy should be canceled.
    if (task == null)
    {
        TrySetCanceled(default(CancellationToken));
        _state = STATE_DONE; // ... and record that we are done
    }

    // ...
}

, , , Task:

var result = await Task.Run(() => (object)null); // Will not throw an exception. result will be null

, TestAsyncExceptionOnlyInTheOutputWindow await , , , .

, (Debug = > Exceptions):

Exceptions

+10

, Task.Run(() = > null),

 public static Task<TResult> Run<TResult>(Func<Task<TResult>> function)

, null, - - ,

Task.Run (()=> (object)null)

Task<TResult> Run<TResult>(Func<TResult> function)

int sample Task.Run(() = > 5); .

public static Task<TResult> Run<TResult>(Func<Task<TResult>> function) 

, .

 public static Task<TResult> Run<TResult>(Func<Task<TResult>> function) 

async . .

.

MSDN

+3

Func<T> , .

    private static async Task TestNullCase()
    {
        // This does not throw a TaskCanceledException
        await Task.Run(() => 5);

        // This does throw a TaskCanceledException
        await Task.Run(() => GetNull());
    }

    private static object GetNull()
    {
        return null;
    }

UPDATE

ReSharper :

    private static async Task TestNullCase()
    {
        // This does not throw a TaskCanceledException
        Func<int> func = () => 5;
        await Task.Run(func);

        // This does throw a TaskCanceledException
        Func<Task> function = () => null;
        await Task.Run(function);
    }

So, the second form is misinterpreted as Func<Task>instead of your intention, which I believe Func<object>. And since the task transferred is null, and since you cannot execute null, you get TaskCanceledException. If you change the type of the variable to Func<object>, it will work without any additional changes.

0
source

All Articles