Click here to Skip to main content
Click here to Skip to main content

Concurrent Observable Collection

By , 11 Apr 2012
 

Introduction

There are plenty of custom implementations of thread safe observable collections. Some are more advanced than others but in most scenarios these are overkill. The overhead introduced by concurrency code in most cases is not necessary. I will demonstrate how the regular ObservableCollection could be used in a multithreaded environment without any need for creating a new type.

Background

In order to use a collection in a thread safe manner, at the very least, there has to be an object which could be used to synchronize access to the collection. Most custom solutions subclass the ObservableCollection in order to associate a sync object with the collection but it is not necessary. ObservableCollection already has that object. It derives from the Collection class which implements the ICollection interface.

public interface ICollection : IEnumerable
{
    int Count { get; }
    bool IsSynchronized { get; }
    object SyncRoot { get; }
    void CopyTo(Array array, int index);
}

The SyncRoot property gives us exactly the object we need. Now synchronization of the collection is only a matter of properly applying locks only where and when it is required. There is no point in wasting any processing time where it is not needed.

Using the code

Using the collections in a thread safe manner is very simple. Just cast it into ICollection and lock on the SyncRoot property:

lock ((_collection as ICollection).SyncRoot)
{
    _collection.Add(newObject);
}

SyncRoot provides a universal way of accessing the synchronization object of the collection. So if you subclass a collection and create your own type, you will still be able to access and use this object and work in concert with whatever synchronization the class provides.

Implementing Concurrency in a derived type

If you wish to implement a thread safe collection as a derived type, it is still beneficial to use the SyncRoot property. It gives you access to the same lock object inside and outside your class, and allows extra flexibility in handling non-trivial situations.

lock((this as ICollection).SyncRoot)
{
    T removedItem = this[oldIndex];
    base.RemoveItem(oldIndex);
    base.InsertItem(newIndex, removedItem);
}

Because lock() internally uses Monitor, it is safe to call lock on the same thread recursively. This allows us to combine several operations under the same external lock:

lock ((_collection as ICollection).SyncRoot)
{
    T removedItem = _collection[Index];
    _collection.RemoveAt(oldIndex);
    _collection.Insert(newIndex, removedItem);
}

When lock is applied to SyncRoot inside RemoveAt, Insert, etc., it will not block. It will increase the reference counter and continue with the operation. Once the operation is done, it will decrease the counter and release the lock once all of them are done.

History

  • 10/27/2011 - Released.
  • 04/11/2012 - Recategorized as Tip

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Eugene Sadovoi
Software Developer (Senior)
United States United States
Member
Senior Software Engineer with over 20+ years of experience in variety of technologies, development tools and programming languages.
 
Microsoft Certified Specialist programming in C#, JavaScript, HTML, CSS

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionConcurrent CollectionsmemberYandyZM31 Oct '11 - 11:21 
What is the diferent betwen this and the concurrent collections in System.Collections.Concurrent like ConcurrentDictionary, ConcurrentQueue, ConcurrentBag, ConcurrentStack, etc. Is this solution better? Or you don't know anything about the Task Parallel Library?
 
In C++, the equivalents are concurent collections for: vector and stack, and in visual studio 11 they add: queue, priorityqueye and map.
AnswerRe: Concurrent Collections [modified]memberEugene Sadovoi31 Oct '11 - 13:44 
YandyZM,
 
I lot of code uses ObservableCollection and Collection classes because it performs much faster than collections from Concurrent namespace. There are other differences as well. For all proctical purposes these are completely different types of collections with different design goals.
 
My intention was not to come up with the best solution for concurrent collection but rather to show how to safelly use regular ObservableCollection and Collection in multithreaded environment.

modified 31 Oct '11 - 21:22.

GeneralRe: Concurrent CollectionsmemberYandyZM1 Nov '11 - 4:43 
Ok, I going to explain you where i use concurrent collections, and please tell me if you think it will perform better using a custom thread safe collection and why.
 
I'm using threads for the process thread pool to get notifications for IO completed tasks. Then i pick up this tasks and enqueue they in a concurrent collection from the concurrent namespace, to procees they later in other threads. Before change to concurrent collections i was using a custom locked collection but i had the feeling that it was creating a bottle neck.
 
From my point of view, the concurrent collections in the concurrent namespace are the right choice. And suposing you create your own concurrent collection and make it work correctly and efficiently through Interlocked operations and ensuring they don't suffer from the AB problem, you will end up getting the same performance.
 
Now, there exist some situations where and inner locking mechanism like those from concurrent collections it is not enough, and you will end up doing external lockings and having the innecesary lock from the concurrent collections. But, for the right task, the concurrent collections from the concurrent namespace are the right choice. Correct me if i am wrong.
AnswerRe: Concurrent CollectionsmemberEugene Sadovoi8 Nov '11 - 3:43 
You are right. There are right tool for every job.
I am using collections from concurrent namespace all the time. The case I describe in my article is rather exception than the common solution.
GeneralMy vote of 4memberSledgeHammer0128 Oct '11 - 7:49 
Convienient, but not really "nice" to have your code littered with lock ((_collection as ICollection).SyncRoot) calls Smile | :) .
AnswerRe: My vote of 4 [modified]memberEugene Sadovoi28 Oct '11 - 7:52 
It does not have to be outside your type. It might be if you require it.
You could use this object in derived type to implement concurrency.
 
Most of the concurrent solutions I've seen so far implement its own instance of SyncRoot. I am trying to point out the fact that standard collections already have this sync object and there are no need to create a new one.

modified 28 Oct '11 - 14:00.

QuestionusefulmemberCIDev28 Oct '11 - 6:25 
This is clearly written and useful; although rather short for an article, more the size of a tip.
Just because the code works, it doesn't mean that it is good code.

AnswerRe: usefulmemberEugene Sadovoi28 Oct '11 - 7:24 
It is a bit shorter than I anticipated. I was going to give few examples with events and dispatcher but decided that it will only distract from the main idea.
GeneralEventsmemberstooboo27 Oct '11 - 23:34 
The subclassing that I've seen for this usually includes raising the collections events on a specific thread ...(normally the UI thread)
So although this will work it doesn't address all the issues
GeneralRe: EventsmemberEugene Sadovoi28 Oct '11 - 4:05 
The focus of this article is this little known way of synchronizing regular collections. Proper handling of events is too complex an issue and beyond this article.
GeneralRe: Eventsmemberstooboo28 Oct '11 - 7:39 
Ok , but I think that most people choosing to use ObservableCollection (rather than other collection classes) will do so because of the CollectionChanged event
 
So which thread that event will actually be raised on is an important consideration if your are going to use it in a multi-threaded way
GeneralRe: EventsmemberEugene Sadovoi28 Oct '11 - 7:49 
stooboo,
 
You are absolutely right. This topic is very important and a lot of people dedicated lots of time explaining it.
Simple search for PropertyChanged and UI[^] returns several pages of useful information on this topic. I should have mentioned in this article that I left this subject out on purpose.
 
Thank you for your feedback.
QuestionSynchronization needs to be handled with carememberJohn Brett27 Oct '11 - 22:43 
I think this article underplays the issue of synchronization, which is potentially one of the most complex we face as developers.
 
The Framework developers themselves are uneasy with the SyncRoot pattern, and have dropped it from the generic collections - this is the reason why you need the typecast. See Questionable value of SyncRoot on Collections[^] by Brad Adams for an explanation.
 
Essentially, the above article says that you need to perform some locking on access to the collection, and that the SyncRoot property gives you a convenient object on which to lock. In turn, what Microsoft are saying is that synchronization is too complex to be handled by the framework, and the application needs to handle locking, based upon its own needs.
 
John
AnswerRe: Synchronization needs to be handled with carememberJulien Villers27 Oct '11 - 23:09 
The article you've linked to doesn't oppose this article's approach. It says that the Framework itself won't be using much SyncRoot/IsSynchronized as it's not efficient for many non-trivial cases. But using it from outside the class as is done here isn't discouraged, at least by the article you've linked here.
'As programmers go, I'm fairly social. Which still means I'm a borderline sociopath by normal standards.' Jeff Atwood
 
'I'm French! Why do you think I've got this outrrrrageous accent?' Monty Python and the Holy Grail

AnswerRe: Synchronization needs to be handled with carememberEugene Sadovoi28 Oct '11 - 4:29 
John,
 

I do not agree with your statement about synchronization being that complex. There are no magic involved and if you follow few well known basic principles it is rather trivial.
This pattern allows you access to the sync object native to the collection. It means that event if collection is subclassed and synchronized you could still provide custom lock when nontrivial requirement exists, for example adding range of elements at once.
 
Brad Adams expressed his opinion and it is just that, opinion. And I agree with him by the way but as he stated, there are no universal solutions.
QuestionThoughtmembercdkisa27 Oct '11 - 14:42 
K.I.S.S. well stated.
AnswerRe: ThoughtmemberEugene Sadovoi28 Oct '11 - 7:36 
Thank you for your feedback!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 11 Apr 2012
Article Copyright 2011 by Eugene Sadovoi
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid