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

时间:2015-04-02 14:53:46

标签: c# .net multithreading collections thread-safety

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

  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以使其成为线程安全的,因为在我的情况下,主线程和任务之间的并发访问可能会导致问题。

我想避免使用并发集合(如here所述),因为我必须在我的程序中使用此集合类型。

那么,如何编辑此实现来完成此任务?最好的想法是什么?

谢谢,

3 个答案:

答案 0 :(得分:2)

如果我理解正确,你只有一个线程修改集合,只是想确保读者可以安全地访问它。

如果是这样,您可以让后台线程计算新元素,但调度会将元素添加到读者线程中。这有效地使变异/读取全部发生在同一线程上,避免了同步问题。我相信这是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()));
}

答案 1 :(得分:1)

我通常使用

private readonly object m_lock = new object();

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

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

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

答案 2 :(得分:1)

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

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

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

相关问题