带有aync回调方法的计时器中的异常

时间:2019-11-14 11:45:29

标签: c# .net multithreading http async-await

我已经继承了一些代码,其中此模式(简化)用于每秒轮询设备,并检查它是否具有通过REST api的新数据:

using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Net.Http;

public class Class
{
    private readonly string _iotIPaddress = "123.456.789.0";
    private System.Timers.Timer _timer = null;
    private readonly HttpClient _client = new HttpClient();

    public Constructor()
    {
        _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(--secrets--);
        _client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        _timer = new System.Timers.Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
        _timer.Elapsed += OnTimer;
    }

    private async void OnTimer(object sender, EventArgs e)
    {
        try
        {
            _timer.Stop();

            var success = await ProcessUpdates();
            // Some stuff depending on whether ProcessUpdates succeeded
        }
        catch (Exception ex)
        {
            // Log the exception
        }
        finally
        {
            _timer.Start();
        }
    }

    private async Task<SomeSettingsStruct> ProcessUpdates()
    {
        try
        {
            string json = await _client.GetStringAsync($"http://{_iotIPaddress}/di_value/slot_0"); //Exception is thrown on this line

            var resultdata = JsonConvert.DeserializeObject<SomeSettingsStruct>(json);

            return resultdata;
        }
        catch (Exception e)
        {
            //Log the exception
            return null;
        }
    }
}

在大多数情况下,这可以正常工作。但是有时连接会滞后(设备通过wifi连接到设备),GetStringAsync抛出一些预期的异常(呵呵),但有时这似乎会导致代码长时间阻塞。这可能表明网络问题持续了超过几分钟,但现场人员并不相信这种情况。因此,问题必须出在代码中。

尤其是这种异步回调方法并不适合我,计时器无论如何都会在不同线程上调用它的回调,那么回调中的异步/等待东西会发生什么?是在计时器线程上调用的,还是它们自己的任务? 这会导致竞争状况,用HttpClient做奇怪的事情吗?

这是我会在日志中看到的例外情况:

  

在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务)在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)在C.的Example.Namespace.Class.d__13.MoveNext() \ dev \ Project \ Example \ Namespace \ Class.cs:第103行

由于我们还记录了FirstChanceExceptions,因此我经常看到这种情况在发生之前:

  

发生异常:由于线程退出或应用程序请求123.456.789.0:80

,I/O操作已中止。

1 个答案:

答案 0 :(得分:2)

  

因此,问题必须出在代码中。

不一定。我认为您的代码完全没有问题。

HttpClient在发生超时时(即,没有响应并且放弃等待时)抛出TaskCanceledException。这不是很直观,people have complained就是这样。

人们have found workarounds使它抛出更好的异常,但是我认为您不必为此烦恼。唯一一次抛出TaskCanceledException的情况是,如果您通过了CancellationToken,则表示已取消。但是您没有传递一个,因此可以确定如果它确实抛出了TaskCanceledException,那是因为超时。

您可以将超时(_client.Timeout)超时(Wireshark)超过默认的100秒,但是如果这是通过不可靠的无线连接进行的,则可能无济于事。

我的赌注是关于网络问题。您可以使用{{3}}进行验证(验证请求已发送,但未收到任何请求)。