【问题标题】:Must an IDictionary implementation that implements INotifyPropertyChanged be thread safe?实现 INotifyPropertyChanged 的​​ IDictionary 实现必须是线程安全的吗?
【发布时间】:2017-11-07 16:59:43
【问题描述】:

我正在查看 this code 以获取我想与 Xamarin.iOS / Android 一起使用的可观察字典。有 cmets 和答案表明并发不是问题。

一般来说,在处理实现 INotifyPropertyChanged 的​​对象时,我应该依赖哪些线程安全保证?

换句话说:

  • 从 UI 线程使用可观察字典时,该对象是否也必须是线程安全的?

  • 如果字典不在 UI 线程上管理怎么办?

我正在考虑另一种实现方式

【问题讨论】:

    标签: multithreading xamarin.ios xamarin.android inotifypropertychanged concurrentdictionary


    【解决方案1】:

    一般来说,在处理实现了 INotifyPropertyChanged 的​​对象时,我应该依赖哪些线程安全保证?

    当一个对象实现INotifyPropertyChanged接口时,它的成员PropertyChanged是一个用于拦截属性改变动作的事件处理程序,应该在我们的代码中实现,然后我们还需要创建方法/函数来处理这个事件,从而确保多个线程在对该对象进行操作时不会发生冲突。例如:

    public class Test1 : INotifyPropertyChanged
    {
        private string _test;
    
        public string test
        {
            get { return _test; }
            set
            {
                if (value != _test)
                {
                    _test = value;
                    OnPropertyChanged();
                }
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged([CallerMemberName]string propertyName = "")
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    现在的问题是PropertyChanged事件如何保证它的安全,如果你使用ILSpy查看INotifyPropertyChanged Interface的源代码,你会发现在它里面,它只是实现了一个委托PropertyChanged,并且委托是这样的:

    using System;
    using System.Security.Permissions;
    
    namespace System.ComponentModel
    {
        /// <summary>Represents the method that will handle the <see cref="E:System.ComponentModel.INotifyPropertyChanged.PropertyChanged" /> event raised when a property is changed on a component.</summary>
        /// <param name="sender">The source of the event. </param>
        /// <param name="e">A <see cref="T:System.ComponentModel.PropertyChangedEventArgs" /> that contains the event data. </param>
        [__DynamicallyInvokable]
        [HostProtection(SecurityAction.LinkDemand, SharedState = true)]
        public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
    }
    

    你应该能发现在这个委托上面有一个HostProtection属性,这个属性允许使用声明性安全动作来确定主机保护要求。在这个HostProtectionAttribute : CodeAccessSecurityAttribute 的实现内部,它创建了线程同步锁。以下是它的部分代码:

    /// <summary>Gets or sets a value indicating whether synchronization is exposed.</summary>
    /// <returns>true if synchronization is exposed; otherwise, false. The default is false.</returns>
    public bool Synchronization
    {
        get
        {
            return (this.m_resources & HostProtectionResource.Synchronization) > HostProtectionResource.None;
        }
        set
        {
            this.m_resources = (value ? (this.m_resources | HostProtectionResource.Synchronization) : (this.m_resources & ~HostProtectionResource.Synchronization));
        }
    }
    
    /// <summary>Gets or sets a value indicating whether shared state is exposed.</summary>
    /// <returns>true if shared state is exposed; otherwise, false. The default is false.</returns>
    public bool SharedState
    {
        get
        {
            return (this.m_resources & HostProtectionResource.SharedState) > HostProtectionResource.None;
        }
        set
        {
            this.m_resources = (value ? (this.m_resources | HostProtectionResource.SharedState) : (this.m_resources & ~HostProtectionResource.SharedState));
        }
    }
    

    所以对于你的问题:

    当使用 UI 线程中的可观察字典时,该对象也必须是线程安全的吗?

    如果字典不在 UI 线程上管理怎么办?

    在UI线程中,如果你的代码中没有跨线程变量访问是安全的,否则会导致不安全。当不在UI线程中时,我们需要使用invoke方式(UI Dispatcher)将动作推送到UI线程。

    【讨论】:

    • 我想使用字典来收集蓝牙发送给我的对象。然后,我将使用 Rx 和等效的计时器扫描并清理该字典。如果这不在 UI 线程中,除了使用并发字典之外,我应该怎么做才能维护事件的安全?
    • 对于这个用例我还有什么需要注意的吗?
    • @LamonteCristo,使用实现INotifyPropertyChanged接口的集合在任何线程中都应该是安全的,正如我所说,如果它在其他线程中并且你想在UI线程中使用它,你可以使用UI调度员,这是一种安全的方式。但是从你的描述来看,你的目标是通过串口连接到安卓设备的蓝牙配件吗?
    • 我将有许多不同的线程来监听 P2P Wifi、BTLe、互联网推送通知和 NFC。平台是 iOS Android 和有限的 WinPhone。所有这些都会将信息推送到带有日期戳的字典中。维护线程将汇总和老化/存档数据。出于调试目的,数据将显示在 TableView 中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-12
    • 1970-01-01
    • 2019-10-10
    • 2010-12-10
    • 2017-07-27
    相关资源
    最近更新 更多