Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/301.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从任意线程调用StateHasChanged()安全吗?_C#_Blazor_Blazor Server Side - Fatal编程技术网

C# 从任意线程调用StateHasChanged()安全吗?

C# 从任意线程调用StateHasChanged()安全吗?,c#,blazor,blazor-server-side,C#,Blazor,Blazor Server Side,从任意线程调用statehaschange()安全吗 让我给你一些背景知识。设想一个服务器端Blazor/Razor组件应用程序,其中您有: 从任意线程引发BreakingNews事件的单例服务NewsProvider 一个组件News.cshtml,用于注入服务并订阅BreakingNews事件。引发事件时,组件更新模型并调用StateHashChanged() NewsProvider.cs 使用系统; 使用系统线程; 名称空间BlazorServer.App { 公共类Breaking

从任意线程调用
statehaschange()
安全吗

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

  • 从任意线程引发
    BreakingNews
    事件的单例服务
    NewsProvider
  • 一个组件
    News.cshtml
    ,用于注入服务并订阅
    BreakingNews
    事件。引发事件时,组件更新模型并调用
    StateHashChanged()
NewsProvider.cs
使用系统;
使用系统线程;
名称空间BlazorServer.App
{
公共类BreakingNewsEventArgs:EventArgs
{
公共只读字符串新闻;
public BreakingNews事件参数(字符串新闻)
{
这个。新闻=新闻;
}
}
公共接口提供程序
{
事件处理程序突发新闻;
}
公共类新闻提供者:INewsProvider,IDisposable
{
私有整数n=0;
公共事件处理程序突发新闻;
私人定时器;
公共新闻提供者()
{
计时器=新计时器(BroadcastingBreakingNews,null,102000);
}
void broadcasting breakingnews(对象状态)
{
BreakingNews?.Invoke(这是新的BreakingNewsEventArgs(“通知”++n));
}
公共空间处置()
{
timer.Dispose();
}
}
}
News.cshtml
@page”/news
@注入INewsProvider新闻提供者
@实现IDisposable
新闻
@foreach(本新闻中的变量n)
{
@n

} @功能{ EventHandler breakingNewsEventHandler; 列表新闻=新列表(); 受保护的覆盖void OnInit() { base.OnInit(); breakingNewsEventHandler=新事件处理程序(OnBreakingNews); this.NewsProvider.BreakingNews+=breakingNewsEventHandler; } void OnBreakingNews(对象发送者,BreakingNewsEventArgs e) { this.news.Add(e.news); StateHasChanged(); } 公共空间处置() { this.NewsProvider.BreakingNews-=breakingNewsEventHandler; } }
Startup.cs
使用Microsoft.AspNetCore.Blazor.Builder;
使用Microsoft.Extensions.DependencyInjection;
使用BlazorServer.App.Services;
名称空间BlazorServer.App
{
公营创业
{
public void配置服务(IServiceCollection服务)
{
//因为Blazor是在服务器上运行的,所以我们可以使用应用程序服务
//读取预测数据。
services.AddSingleton();
services.AddSingleton();
}
public void配置(IBlazorApplicationBuilder应用程序)
{
app.AddComponent(“app”);
}
}
}

它显然有效,但我不知道
statehaschange()
是否是线程安全的。如果不是,如何安全地调用
StateHashChanged()
?。是否有类似于Control.BeginInvoke的功能?我应该使用
SyncrhonizationContext.Post

否,从任意线程调用
statehaschange()
是不安全的

调用
statehaschange()
的正确方法是使用


从ASP.NET Core 3.1预览版2开始,Invoke()在ComponentBase中不再可用。你应该使用InvokeAsync()来代替teadi-believe,但你知道这是否有正式的文档记录或者有一篇关于这方面的文章。。。我的意思是,我不是要支持你所说的,但我想更深入地理解为什么D:@DanielVega用户界面通常不是线程安全的,Blazor也不例外。如果StateHasChanged不是线程安全的,应该包装在InvokeAsync中,我会回避这个问题:1)为什么不允许InvokeAsync在扩展中,2)为什么不首先让StateHasChanged线程安全?
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");
        }
    }
}
void OnBreakingNews(object sender, BreakingNewsEventArgs e)
{
    InvokeAsync(() => {
        news.Add(e.News);
        StateHasChanged();
    });
}