简体   繁体   中英

Make custom C# collection thread-safe

I create this specific collection 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));
        }

    }

then I'd like to use it like this :

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

I'd like to custom RangeObservableCollection to make it thread-safe because in my case,concurrent access between principal thread and task can cause problem.

I'd like to avoid using a concurrent collection (as answered here ) because I have to use this collection type in my program.

So,how can I edit this implementation to accomplish this task?What is the best idea?

Thanks,

If I understand correctly, you only have a single thread modifying the collection and just want to make sure the readers can access it safely.

If so, you can have your background thread compute new elements, but dispatch adding the elements to your reader thread. This effectively makes mutating/reading all take place on the same thread, avoiding the issue of synchronization. I believe this is what Servy was suggesting in the comments.

This is easiest if you're reading from the UI thread, as you can use the standard dispatching mechanisms. Async/await makes this particularly easy (assuming FillList is called from the UI thread):

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

I usually use

private readonly object m_lock = new object();

And every time you are accessing your method do the following:

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

Using this will ensure that only one thread at a time can access this piece of code. Hope this helps

You cannot make this custom collection thread-safe because it inherits from a class that is not thread safe. You may be able to make some of the methods thread-safe (the ones you can override or the ones you write yourself) but not all of them (not all of the members of the base class are virtual).

If you wish to make a custom collection that is thread safe, you should inherit from one of the classes in the System.Collections.Concurrent namespace.

Another option is to use a container approach instead of inheritance, eg write a thread-safe wrapper instead of a descendent class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM