Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/apache-kafka/3.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# 异步服务初始化的竞争条件_C#_Wpf_Asynchronous_Mvvm_Singleton - Fatal编程技术网

C# 异步服务初始化的竞争条件

C# 异步服务初始化的竞争条件,c#,wpf,asynchronous,mvvm,singleton,C#,Wpf,Asynchronous,Mvvm,Singleton,我在C#WPF中工作,使用一个专有框架(基本上是Caliburn Micro和Castle Windsor的混合),我有两个具有竞争条件的单例模块: DeviceService-管理与发送数据的物理设备的连接的服务。服务是“可启动的”,因此会自动构造并异步初始化 ConnectionIndicatorViewModel-一种客户端视图模型,主要涉及向用户传达由设备服务管理的连接状态。更改状态主要基于设备服务触发的事件 我的问题在于应用程序启动。在ViewModel的构造函数中,我将默认状态设置为

我在C#WPF中工作,使用一个专有框架(基本上是Caliburn Micro和Castle Windsor的混合),我有两个具有竞争条件的单例模块:

DeviceService
-管理与发送数据的物理设备的连接的服务。服务是“可启动的”,因此会自动构造并异步初始化

ConnectionIndicatorViewModel
-一种客户端视图模型,主要涉及向用户传达由设备服务管理的连接状态。更改状态主要基于设备服务触发的事件

我的问题在于应用程序启动。在ViewModel的构造函数中,我将默认状态设置为“Pending”,因为我假设服务尚未完成初始化。然后ViewModel只处理服务触发的“初始化”事件。在这个处理程序中,我通过服务上的属性评估实际连接状态,并更新ViewModel

现在,所有这一切都很好,因为比赛条件极不可能出现问题。但是,在不太可能的情况下,服务在构建ViewModel之前完成了初始化,它将永远不会处理“已初始化”事件,而只会停留在“挂起”状态

我考虑过更改服务接口以返回属性的可等待类型,因此任何试图访问属性的模块都必须等待初始化完成,但我不确定这是否是最佳方法。我还担心部分客户端启动服务,因为如果有几个模块使用它,那么谁应该初始化它呢


是否有一些常规的方法来处理我所缺少的这种异步初始化?

您提到使用事件来进行服务和ViewModel之间的通信,您可以使用反应式扩展(Rx)而不是使用事件,这样就可以消除您上面描述的争用条件

简单地说,这将服务从拉模型转换为推模型,它将通过流推出数据\事件,并允许您在流上组合LINQ查询。如果你不熟悉Rx,有很多好的信息

在这个使用Rx的场景中,我会让服务公开一个属性
IObservable
;,其中T是您的类型(我猜是某种状态枚举),此属性的支持字段是重要部分,它将是一个大小为1的
ReplaySubject
。这意味着,任何时候,有人“订阅”该属性,他们将收到发布到该主题的最后一个值。因此,这意味着在发布和订阅流之间不存在竞争条件

这在代码中可能更容易理解:

public enum State
{
    Initializing,
    Initialized,
}

public interface IMyService
{
    IObservable<State> Status { get; }
}

public class MyService : IMyService
{
    private ReplaySubject<State> _state;

    public MyService()
    {
        _state = new ReplaySubject<State>(1);

        _state.OnNext(State.Initializing);

        // Do initialisation stuff

        _state.OnNext(State.Initialized);

    }
    public IObservable<State> Status { get { return _state;  } }
}
现在,服务和ViewModel之间没有竞争条件


关于反应式扩展有很多需要了解的地方,但在实现MVVM应用程序时,这是处理异步调用的一种非常好的方法。

当ViewModel依赖于服务时,它可以检查服务的Initialized属性,看服务是否准备就绪。如果准备好了,开始抓取。如果不是,ViewModel将等待事件被接收。我也考虑过这一点,但是(如果我不玩弄锁的话)这不会导致不确定性吗?考虑“代码初始化”事件在“代码”>初始化后的属性检查之后,但在设置状态之前启动。这可能导致正确的连接状态被“挂起”状态覆盖。Hmm。您可以通过检查IsInitialized来检查服务是否已经初始化->如果结果为false=>Pending=true->等待事件->初始化事件=>在处理程序和访问服务内部设置Pending=false。当事件得到处理时,状态总是设置回not pending(未挂起)。抱歉,我不确定我是否理解正确。如果服务从其他线程回调并访问共享成员,您可以应用锁。如果设置挂起属性(线程A),而另一个具有较高存在性的线程(线程B)同时写入该属性,则可能会覆盖更相关的值(线程B)。优先级;)检查IsInitialized是否为false。如果为false,则在最终设置一个依赖值之前,应用一个锁并再次检查(双重检查)。非常好!这是我第一次听说Rx。我喜欢。它看起来很有魅力,但我不得不用调度器替换调度器。默认值(
Scheduler.CurrentThread
)来替换调度器。看起来主库的NuGet包不包含线程dll,我不完全确定在包管理器的何处可以找到它。很抱歉,忘了提及它已移动到单独的命名空间,请参阅此问题
public class MyViewModel
{
    private State _state;

    public MyViewModel(IMyService myService)
    {
        myService.Status.ObserveOn(DispatcherScheduler.Current)
            .Subscribe(x =>
                        {
                            _state = x;
                        });
    }

    public bool IsReady { get { return _state == State.Initialized; } }
}