代码之家  ›  专栏  ›  技术社区  ›  Jesús López Sean Lange

从任意线程调用StateHasChanged()是否安全?

  •  2
  • Jesús López Sean Lange  · 技术社区  · 6 年前

    打电话安全吗 StateHasChanged() 从任意线程?

    让我给你一些背景。设想一个服务器端Blazor/Razor组件应用程序,其中您有:

    • 单人服务 NewsProvider 提出 BreakingNews 来自任意线程的事件。
    • 一个组成部分 News.cshtml 使服务注入并订阅 大事件 事件。当事件发生时,组件更新模型并调用 StateHashChanged()

    新闻提供者

    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();
            }
        }
    }
    

    新一代

    @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;
        }
    }
    

    创业公司

    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");
            }
        }
    }
    

    很明显是有效的,但我不知道 状态已更改() 是线程安全的。如果不是,我怎么打 StateHashChanged()。 安全吗?有类似的东西吗 Control.BeginInvoke ?我应该用吗? SyncrhonizationContext.Post ?

    2 回复  |  直到 6 年前
        1
  •  0
  •   Kirk Woll    6 年前

    也许这能帮助你…

    SteveSanderson:

    现有的用户界面更新机制是围绕着从.NET到JS传输最小差异的批处理而设计的,因此,让它将这些差异表示为序列化数据而不是指向WASM内存空间的指针不太难。我们必须小心处理事件,以确保异步不会导致行为与同步单线程模型不一致。例如,就JS端而言,我们需要强制所有事件传递都是异步的,即使在非工作线程的情况下也是如此。

    来源: https://github.com/aspnet/AspNetCore/issues/5475

        2
  •  0
  •   Jesús López Sean Lange    6 年前

    不,叫 StateHasChanged() 来自任意线程是不安全的。在ASP.NET Core 3.0 Preview 2上运行该代码会引发以下异常:

    Microsoft.aspnetcore.components.browser.rendering.RemoteReprenderException: '当前线程与渲染器的 同步上下文。使用invoke()或invokeAsync()切换 触发时执行到渲染器的同步上下文 渲染或修改渲染期间访问的任何状态。

    正确的呼叫方式 状态已更改() 如下:

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

    但是 Invoke 已添加到ASP.NET Core 3.0预览Razor组件中,它在ASP.NET Core 2.1服务器端Blazor上不可用。