C# Concurrency – Thread Safety and Efficiency with ReaderWriterLockSlim

cconcurrencylocks

I have a list, to which I write once in every update Loop and then I spawn some tasks which create read that List and create a duplicate to manipulate that data.
More or less it's List.ToList() call to create a new list instead of using it by reference. Each task manipulates that data in it's own and produces it's results.
Currently I am using a traditional lock in the form of

lock(LockObject){
//write stuff
}

when writing and

lock(LockObject){
localList=List.ToList();
}

when reading.

Would using the ReaderWriterLockSlim object instead of the way I am doing it now provide me with any benefits speed-wise? Also is the ToList() method considered a read in this case?

Clarification Edit

  1. The readers create a copy because they manipulate the data
    differently, each one of them removes or adds data to the List on
    their own way. (some might add previous data they still need while
    others remove data they do not need. Also the update loop occurs
    once every few seconds so it is common.
  2. Sometimes if the data input is really fast an update/write loop might start before all the readers have completed their processing (but if they get the updated data instead is no problem for me).
  3. Basically the readers are there for presenting current data to
    others. So if data is changed before they even start presenting them
    that's ok. As long as they do not try to process data while they are
    being update (which would probably throw an exception)

Best Answer

It depends. Using a reader/writer lock rather than a critical section can be beneficial if read operations are noticeably more common than write operations. But I think you could probably achieve more by changing your application architecture.

If writes are rare, you can probably get much better performance by treating the collection as immutable and producing an entirely new list when it changes. You can then use Interlocked.CompareExchange to update the reference to the list in a thread-safe way. This should prevent readers needing to make a defensive copy (which should be a huge win if reading is more common than writing) and remove the need for locking.

Alternatively, if writing is more common than reading, it may be worth evaluating a ConcurrentStack as a replacement for the list. This provides an atomic ToArray method that can be used to make copies without needing locks. Alternatively, if order isn't important in your list, a ConcurrentBag may perform better.

Edit after clarification

I still think you would be better off copying the list when you modify it, as this allows you to do away with the lock entirely. Even if the readers need to change the list contents during processing, this would be better refactored to use a streaming style, e.g. LINQ to Objects, so that these operations do not actually duplicate the data. See Martin Fowler Refactoring with Loops and Collection Pipelines for more about converting existing code to this style.

Failing that, I imagine you'd see a small gain switching to the reader/writer lock, but unless lock contention is unusually problematic for your application I doubt it'll be noticeable.

Related Topic