Catch exception in async Task

2020-08-01 11:26发布

问题:

I'm working in C# (console application).

In my program, I have to contact a httpClient. First I check if the client is responding using GetAsync. So my request method is async, making my Task async.

When client doesn't respond (or something else) it throw an exception but i'm unable to catch it.

I added a ContinueWith but it doesn't work. With a breackpoint I saw that the piece of code is reached at the start of my Task so the exception is always null.

How can I solve this problem ?

There is my code :

static void Run()
{
    String urlRequest = "";
    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;

    Console.WriteLine($"Program running, press a key to stop");
    try
    {
        Task task = Task.Factory.StartNew(async () =>
        {
            using (HttpClientHandler handler = new HttpClientHandler { Credentials = new NetworkCredential("user", "pass") })
            {
                HttpClient client = new HttpClient(handler);
                client.BaseAddress = new Uri(urlRequest);
                client.DefaultRequestHeaders.Accept.Clear();
                bool serviceAvailable = await CheckService(client);
                if (serviceAvailable)
                {
                    bool doLoop = true;
                    while (doLoop)
                    {
                        // Do something

                        Thread.Sleep(100);
                        if (ct.IsCancellationRequested)
                        {
                            Console.WriteLine("\r\ntask cancelled");
                            break;
                        }
                    }
                }
                else
                {
                    throw new HttpRequestException($"Unable to contact service at {urlRequest}");
                }
            }

        }, ct).ContinueWith(tsk =>
        {
            if (tsk.Exception != null)
                throw tsk.Exception;
        });

        Console.ReadKey();
        cts.Cancel();
        Thread.Sleep(1000);
    }
    catch (Exception e)
    {
        Log(e);
    }
}

static async Task<bool> CheckClient(HttpClient client)
{
    Console.WriteLine("Check service Call ...");
    HttpResponseMessage response = await client.GetAsync("CheckService");
    if (response.IsSuccessStatusCode)
    {
        return true;
    }

    return false;
}

回答1:

You are neither waiting for nor observing the result of your task. Assuming that you're running this against .Net 4.5 or later, the exception is there but it is going unnoticed.

So, first of all, you should .Wait() for your task to complete so that the calling code has a chance to observe the exception.

Generally speaking, you should avoid using Task.Factory.StartNew() and prefer Task.Run() instead - see Stephen Toub's explanation why. But if you prefer to use Task.Factory.StartNew for whatever reason, then you additionally must first .Unwrap() the original task to obtain the actual task for which you want the result. (Do note that .Unrwap() is an extension method that is available for Task<Task> but not Task, so type the original variable accordingly; i.e., use var task = Task.Factory.StartNew(...))



回答2:

ContinueWith will be executed when the Task completed, with or without success.

In the case of success, your tsk.Exception in ContinueWith will be null.

Can you try :

.StartNew(...)
.ContinueWith(tsk =>
        {
            //thrown
        },TaskContinuationOptions.OnlyOnFaulted);