如何在没有SynchronizationContext的情况下使用INotifyPropertyChanged进行线程安全?

Ste*_*hen 1 c# multithreading inotifypropertychanged

如何让您的对象保持线程安全,实现INotifyPropertyChanged?我不能使用SynchronizationContext,因为我需要能够序列化对象.

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
           // What can I add here to make it thread-safe? 
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
Run Code Online (Sandbox Code Playgroud)

Bob*_*Bob 6

所以......如果您不介意在编译时依赖于为您生成一些代码的扩展,那么实际上这是一种非常好的方法.无论如何,我正在使用Fody/PropertyChanged,这是一个非常容易的变化.这避免了必须引用SynchronizationContext真正没有业务知道UI的模型.

  1. 首先,安装NuGet提供的PropertyChanged.Fody.

  2. 当前实现的每个类INofityPropertyChanged都应该具有该属性[ImplementPropertyChanged].应删除手动实施.有关更多信息,请参阅自述文件维基.

  3. 一个PropertyChangedNotificationInterceptor需要实现.它们为wiki中的WPF提供了一个示例; 我对WinForms项目的实现:

    public static class PropertyChangedNotificationInterceptor
    {
        public static SynchronizationContext UIContext { get; set; }
    
        public static void Intercept(object target, Action onPropertyChangedAction, string propertyName)
        {
            if (UIContext != null)
            {
                UIContext.Post(_ =>
                {
                    onPropertyChangedAction();
                }, null);
            }
            else
            {
                onPropertyChangedAction();
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 设置在PropertyChangedNotificationInterceptor.UIContext某个地方.您可以放入PropertyChangedNotificationInterceptor.UIContext = WindowsFormsSynchronizationContext.Current;主窗体的构造函数.

    Form构造穿过控制构造,并最终结束了创建WindowsFormsSynchronizationContext,如果一个不存在.因此,捕获.Current这里是安全的.(注意:这可能是一个实现细节,可能会在将来的.NET版本,不同的平台等中更改)

请记住,只有在您只有一个同步上下文(单个UI线程)时,这才有效.如果您遇到多个UI线程的混乱,并且需要多个数据绑定,这将变得更加复杂.所以请不要这样做.

感谢Simon Cropp的写作PropertyChanged,并帮助我找到它的这个特殊功能.


Dan*_*sha 5

如果您使用的是 WPF,则可以使用 Dispatcher 封送对 UI 线程的调用。

App.Current.Dispatcher.Invoke(new Action(()=>{ 
    if (handler != null)
        handler(this, new PropertyChangedEventArgs(propertyName));
}));
Run Code Online (Sandbox Code Playgroud)

  • 一个小时的搜索......终于 - 一个优秀的,干净的,简洁的和有效的答案(无论如何对于wpf)。谢谢! (2认同)

小智 5

如果您不幸运并且必须使用 Winforms,请尝试使用应用程序的 MainForm 来调用 UI 线程中的处理程序。不好的是你必须包括

using System.Windows.Forms;

protected void OnPropertyChanged(string propertyName)
{
    var handler = PropertyChanged;
    if (handler != null)
    {
        if (Application.OpenForms.Count == 0) return; 
        var mainForm = Application.OpenForms[0];
        if(mainForm == null) return; // No main form - no calls

        if (mainForm.InvokeRequired) 
        {
            // We are not in UI Thread now
            mainform.Invoke(handler, new object[] {
               this, new PropertyChangedEventArgs(propName)});
        }
        else
        {
            handler(this, new PropertyChangedEventArgs(propertyName)); 
        }              
    }
}
Run Code Online (Sandbox Code Playgroud)