Is it safe to call StateHasChanged() from an arbitrary thread?

别说谁变了你拦得住时间么 提交于 2020-02-20 07:12:48

问题


Is it safe to call StateHasChanged() from an arbitrary thread?

Let me give you some context. Imagine a Server-side Blazor/Razor Components application where you have:

  • A singleton service NewsProvider that raises BreakingNews events from an arbitrary thread.
  • A component News.cshtml that gets the service injected and subscribes to BreakingNews event. When the event is raised, the component updates the model and calls 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");
        }
    }
}

it apparently works, but I don't know if StateHasChanged() is thread safe. If it isn't, how can I call StateHashChanged() safely?. Is there something similar to Control.BeginInvoke? Should I use SyncrhonizationContext.Post?


回答1:


No, calling StateHasChanged() from an arbitrary thread is not safe. Running that code on ASP.NET Core 3.0 preview 2 throws the following exception:

Microsoft.AspNetCore.Components.Browser.Rendering.RemoteRendererException: 'The current thread is not associated with the renderer's synchronization context. Use Invoke() or InvokeAsync() to switch execution to the renderer's synchronization context when triggering rendering or modifying any state accessed during rendering.'

The correct way to call StateHasChanged() is as follows:

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

But Invoke was added to ASP.NET NET Core 3.0 preview Razor Components, it is not available on ASP.NET Core 2.1 Server Side Blazor.



来源:https://stackoverflow.com/questions/54496040/is-it-safe-to-call-statehaschanged-from-an-arbitrary-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!