Безопасно ли вызывать StateHasChanged() из произвольного потока?

Это безопасно звонить StateHasChanged() из произвольной темы?

Позвольте мне дать вам некоторый контекст. Представьте себе серверное приложение Blazor/Razor Components, где у вас есть:

  • Единый сервис NewsProvider что поднимает BreakingNews события из произвольного потока.
  • Компонент News.cshtml который получает сервис введен и подписывается на BreakingNews событие. При возникновении события компонент обновляет модель и вызывает StateHashChanged()

NewsProvider.cs

using System;
using System.Threading;

namespace BlazorServer.App
{
    public class BreakingNewsEventArgs: EventArgs
    {
        public readonly string News;

        public BreakingNewsEventArgs(string news)
        {
            this.News = news;
        }
    }

    public interface INewsProvider
    {
        event EventHandler<BreakingNewsEventArgs> BreakingNews;
    }

    public class NewsProvider : INewsProvider, IDisposable
    {

        private int n = 0;

        public event EventHandler<BreakingNewsEventArgs> BreakingNews;
        private Timer timer;

        public NewsProvider()
        {
            timer = new Timer(BroadCastBreakingNews, null, 10, 2000);

        }

        void BroadCastBreakingNews(object state)
        {
            BreakingNews?.Invoke(this, new BreakingNewsEventArgs("Noticia " + ++n));
        }

        public void Dispose()
        {
            timer.Dispose();
        }
    }
}

News.cshtml

@page "/news"
@inject INewsProvider NewsProvider
@implements IDisposable

<h1>News</h1>

@foreach (var n in this.news)
{
    <p>@n</p>
}


@functions {
    EventHandler<BreakingNewsEventArgs> breakingNewsEventHandler;

    List<string> news = new List<string>();

    protected override void OnInit()
    {
        base.OnInit();
        breakingNewsEventHandler = new EventHandler<BreakingNewsEventArgs>(OnBreakingNews);
        this.NewsProvider.BreakingNews += breakingNewsEventHandler;
    }

    void OnBreakingNews(object sender, BreakingNewsEventArgs e)
    {
        this.news.Add(e.News);
        StateHasChanged();
    }

    public void Dispose()
    {
        this.NewsProvider.BreakingNews -= breakingNewsEventHandler;
    }
}

Startup.cs

using Microsoft.AspNetCore.Blazor.Builder;
using Microsoft.Extensions.DependencyInjection;
using BlazorServer.App.Services;

namespace BlazorServer.App
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // Since Blazor is running on the server, we can use an application service
            // to read the forecast data.
            services.AddSingleton<WeatherForecastService>();
            services.AddSingleton<INewsProvider, NewsProvider>();
        }

        public void Configure(IBlazorApplicationBuilder app)
        {
            app.AddComponent<App>("app");
        }
    }
}

это, очевидно, работает, но я не знаю, StateHasChanged() Поток безопасен. Если это не так, как я могу позвонить StateHashChanged() безопасно?. Есть ли что-то похожее на Control.BeginInvoke? Должен ли я использовать SyncrhonizationContext.Post?

2 ответа

Решение

Нет звоню StateHasChanged() от произвольного потока не является безопасным. Запуск этого кода в ASP.NET Core 3.0 preview 2 приводит к следующему исключению:

Microsoft.AspNetCore.Components.Browser.Rendering.RemoteRendererException: "Текущий поток не связан с контекстом синхронизации средства визуализации. Используйте Invoke() или InvokeAsync(), чтобы переключить выполнение в контекст синхронизации рендерера при запуске рендеринга или изменении любого состояния, доступного во время рендеринга. '

Правильный способ позвонить StateHasChanged() как следует:

void OnBreakingNews(object sender, BreakingNewsEventArgs e)
{
    Invoke(() => {
        news.Add(e.News);
        StateHasChanged();
    });
}

Но Invoke был добавлен в компоненты Razor для предварительного просмотра ASP.NET NET Core 3.0, он недоступен в ASP.NET Core 2.1 на стороне сервера Blazor.

Возможно, это может помочь вам...

SteveSanderson:

Существующий механизм обновления пользовательского интерфейса разработан для передачи пакетов минимальных различий из.NET в JS, поэтому не должно быть огромным затруднением, чтобы он представлял эти различия в виде сериализованных данных, а не указателей в пространство памяти WASM. Мы должны быть осторожны с такими вещами, как обработка событий, чтобы гарантировать, что асинхронность не может привести к несогласованности поведения по сравнению с синхронной однопоточной моделью. Например, нам нужно будет заставить всю доставку событий быть асинхронной, что касается стороны JS, даже в случае не рабочего потока.

Источник: https://github.com/aspnet/AspNetCore/issues/5475

Другие вопросы по тегам