2015-04-02 59 views
-1

我创建这个特定的集合类:使自定义的C#集合线程安全

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),因为我必须在我的程序中使用此集合类型。

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

感谢,

+4

当你说“线程安全”时,你究竟想要达到什么目的?你想同时修改集合吗?你想在另一个线程写入集合时能够使用IEnumerable吗?取决于你想要发生的事情会影响你需要做的事情。 – 2015-04-02 14:57:02

+0

@ScottChamberlain我的意思是我希望能够使用IEnumerable而另一个线程写入它 – 2015-04-02 14:58:56

+1

为什么这个集合需要关注这个?很可能你只需要这种类型的用户同步他们的访问权限,而不是让这个集合尝试管理它。 – Servy 2015-04-02 15:02:08

回答

2

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

如果是这样,你可以让你的后台线程计算新的元素,但派遣添加元素到你的读者线程。这有效地使变异/读取全部发生在相同的线程上,避免了同步的问题。我相信这是塞维在评论中提出的建议。

这是最容易的,如果你正在从UI线程读取,因为你可以使用标准的调度机制。 Async/await使得这非常容易(假设FillList是从UI线程调用的):

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())); 
} 
+0

如果您在FillList开始时启动了3个任务,并保留了作为变量返回的任务,然后等待所有3个启动后的任务,那么可以使函数稍微平行一些。 – 2015-04-02 15:58:46

0

我通常使用

private readonly object m_lock = new object(); 

而且每次要访问你的方法做到以下几点:

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

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

+1

请注意,这只会使每个方法原子;如果使用任何关于集合状态的假设来调用多个方法,这仍然会有问题。 – Servy 2015-04-02 15:01:06

+0

当您的集合被多个类使用时,锁很难使用。 – 2015-04-02 15:01:42

+0

@LamloumiAfif确实;这就是为什么你应该尽一切可能避免在多线程之间尽可能共享状态,并且当你被迫使用时限制该状态的范围尽可能小。 – Servy 2015-04-02 15:04:38

1

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

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

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