C# асинхронное / ожидание недопонимания потока - PullRequest
0 голосов
/ 29 февраля 2020

Я пишу сервис, который отображает некоторые данные. Данные извлекаются из хранилища BLOB-объектов клиентом, у которого есть только метод async Get для получения данных. Как только данные извлечены, они используются для заполнения диаграммы, и это делается с помощью другого метода Redraw, который также async. Я хотел бы, чтобы график обновлялся каждую минуту или около того, поэтому у меня есть Get и Redraw, вызываемые через некоторое время l oop с задержкой.

private async Task Redraw(CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        var data = await DataProvider.Get();

        await Chart.Redraw(data.Labels, new[] {data.DataSet});

        await Task.Delay(TimeSpan.FromSeconds(60), cancellationToken);
    }
}

К сожалению, это не удается. Я получаю NullReferenceException и AggregateException где-то в библиотеке диаграмм. Изменение кода следующим образом работает.

private async Task Redraw(CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        var data = Task.Run(() => DataProvider.Get()).GetAwaiter().GetResult();

        await Task.WhenAll(Chart.Redraw(data.Labels, new[] {data.DataSet}));

        await Task.Delay(TimeSpan.FromSeconds(60), cancellationToken);
    }
}

Я не уверен, почему первая версия, использующая async / await, терпит неудачу. Я полагаю, что это потому, что Redraw пытается запустить до завершения задачи данных, но я также полагал, что ожидание метода Get будет означать, что Redraw не будет вызываться, пока задача данных не будет завершена. Когда я отлаживаю эти строки, кажется, что данные заполняются до того, как будет выполнена следующая строка, поэтому я немного запутался.

Исключение: «System.NullReferenceException» в Blazorise.Charts.dll Exception брошено: «System.NullReferenceException» в System.Private.CoreLib.dll «iis express .exe» (CoreCLR: clrhost): загружено «C: \ Program Files \ dotnet \ shared \ Microsoft.NETCore.App \ 3.1 0,1 \ System.Diagnostics.StackTrace.dll. 'iis express .exe' (CoreCLR: clrhost): загружен 'C: \ Program Files \ dotnet \ shared \ Microsoft.NETCore.App \ 3.1.1 \ System.Reflection.Metadata.dll'. 'iis express .exe' (CoreCLR: clrhost): загружен 'C: \ Program Files \ dotnet \ shared \ Microsoft.NETCore.App \ 3.1.1 \ System.Collections.Immutable.dll'. Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer: Предупреждение: компонент обработки необработанного исключения: ссылка на объект не установлена ​​на экземпляр объекта.

System.NullReferenceException: ссылка на объект не установлена ​​на экземпляр объекта , at Blazorise.Charts. JS .ToChartDataSet [T] (ChartData 1 data) at Blazorise.Charts.JS.InitializeChart[TItem,TOptions](IJSRuntime runtime, DotNetObjectReference 1 dotNetObjectReference, логическое значение hasClickEvent, логическое значение hasHoverEvent, строковое canvasId, тип ChartType, ChartData 1 data, TOptions options, String dataJsonString, String optionsJsonString) at Blazorise.Charts.BaseChart 4.Initialize ()
в. BaseChart`4.OnAfterRenderAsyn c (Boolean firstRender) Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost: Ошибка: необработанное исключение в цепи 'v_kcV7GT8jgmXOLCuNjM_i918pfAqsa27 *. (Ссылка на объект не установлена ​​на экземпляр объекта.) ---> System.NullReferenceException: Ссылка на объект не установлена ​​на экземпляр объекта. В Blazorise.Charts. JS .ToChartDataSet [T] (ChartData 1 data) at Blazorise.Charts.JS.InitializeChart[TItem,TOptions](IJSRuntime runtime, DotNetObjectReference 1 dotNetObjectReference, Boolean hasClickEvent, Boolean hasHoverEvent, String canvasId, тип ChartType, ChartData 1 data, TOptions options, String dataJsonString, String optionsJsonString) at Blazorise.Charts.BaseChart 4.Initialize ()
в Blazorise.Charts.BaseChart`4.OnAfterRenderAsyn c (логическое значение firstRender End End трассировка стека исключений ---

копаю глубже и я не думайте, что нулевая ссылка вообще выбрасывается при вызове Chart.Redraw. Когда эта строка выполняется, данные не равны нулю. Вместо этого похоже, что это как-то связано с async / await. Вызов, предшествующий Redraw, выглядит следующим образом ..

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (!_isAlreadyInitialized && firstRender)
    {
        _isAlreadyInitialized = true;

        await Redraw(_cancellationToken);
    }
}

Это переопределяет метод базового класса из библиотеки. Реализация базового класса выглядит следующим образом ..

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    await this.Initialize();
  else
    await this.Update();
}

Именно этот вызов Initialize вызывает другой метод ..

private static object ToChartDataSet<T>(ChartData<T> data)
{
  return (object) new
  {
    Labels = data.Labels,
    Datasets = ((IEnumerable<object>) data.Datasets).ToList<object>()
  };
}

.. и именно здесь находятся данные имеет значение null.

По некоторым причинам использование await / asyn c в методе Redraw приводит к тому, что базовый класс вызывает Initialize => ToChartDataSet до завершения вызова DataProvider.Get. При использовании GetAwaiter().GetResult(), тогда Initialize не вызывается, пока DataProvider.Get не завершится.

...