使自定义 C# 集合线程安全

Make custom C# collection thread-safe

我创建了这个特定的集合 class :

  public class RangeObservableCollection<T> : ObservableCollection<T>
    {
        private bool _suppressNotification = false;

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            try
            {
                if (!_suppressNotification)
                    base.OnCollectionChanged(e);
            }
            catch  
            {

            }


        }

        public void AddRange(IEnumerable<T> list)
        {
            if (list == null) { 
            return ;
                              }

            _suppressNotification = true;

            foreach (T item in list)
            {

                    Add(item);

            }
            _suppressNotification = false;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public void EditElement(int index, object value)
        {
            _suppressNotification = true;
            if (index >= 0 && index < this.Count) this[index] = (T)value;
            _suppressNotification = false;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
        public void RemoveElementAt(int index)
        {
            _suppressNotification = true;
            if (index >= 0 && index < this.Count) this.RemoveAt(index);
            _suppressNotification = false;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
        public void RemoveElement(T element)
        {
            _suppressNotification = true;
            this.Remove(element);
            _suppressNotification = false;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

    }

那么我想这样使用它:

         public RangeObservableCollection<vue_medecin > MedecinGDP { get; set; }

        public RangeObservableCollection<gdp_groupe > CodeGDP_Collection { get; set; }

        public RangeObservableCollection<fsign_fiche > FiltredParticipant { get; set; }
    private async Task  FillList()
            {

                 await Task.Factory.StartNew(() =>
                                               {
                    gdpList = SimpleIoc.Default.GetInstance<ICrud<gdp_groupe>>().GetAll().ToList();
                    MedecinGDP.AddRange(SimpleIoc.Default.GetInstance<ICrud<vue_medecin>>().GetAll());
                    CodeGDP_Collection.AddRange(gdpList);
                    FiltredParticipant.AddRange(SimpleIoc.Default.GetInstance<ICrud<fsign_fiche>>().GetAll());
                });
            }

我想自定义 RangeObservableCollection 使其成为线程安全的,因为在我的例子中,主线程和任务之间的并发访问可能会导致问题。

我想避免使用并发集合(如回答),因为我必须在我的程序中使用这种集合类型。

那么,我该如何编辑这个实现来完成这个任务?最好的主意是什么?

谢谢,

我一般用

private readonly object m_lock = new object();

每次访问您的方法时,请执行以下操作:

lock(m_lock){
     // your code goes here
}

使用它可以确保一次只有一个线程可以访问这段代码。希望这有帮助

您不能使此自定义集合线程安全,因为它继承自非线程安全的 class。您也许可以使某些方法线程安全(您可以覆盖的方法或您自己编写的方法)但不是所有方法(并非基 class 的所有成员都是虚拟的)。

如果您希望创建一个线程安全的自定义集合,您应该继承 System.Collections.Concurrent 命名空间中的 classes 之一。

另一种选择是使用容器方法而不是继承,例如写一个线程安全的包装器而不是后代 class.

如果我没理解错的话,你只有一个线程在修改集合,只是想确保 reader 可以安全地访问它。

如果是这样,您可以让后台线程计算新元素,但分派将元素添加到 reader 线程。这有效地使 mutating/reading 都发生在同一个线程上,避免了同步问题。我相信这就是 Servy 在评论中提出的建议。

如果您从 UI 线程读取,这是最简单的,因为您可以使用标准调度机制。 Async/await 使这特别容易(假设从 UI 线程调用 FillList):

private async Task  FillList()
{
      gdpList = await Task.Run(() => SimpleIoc.Default.GetInstance<ICrud<gdp_groupe>>().GetAll().ToList());
      MedecinGDP.AddRange(await Task.Run(() => SimpleIoc.Default.GetInstance<ICrud<vue_medecin>>().GetAll()));
      CodeGDP_Collection.AddRange(gdpList);
      FiltredParticipant.AddRange(await Task.Run(() => SimpleIoc.Default.GetInstance<ICrud<fsign_fiche>>().GetAll()));
}