Безопасно ли вызывать 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, даже в случае не рабочего потока.