Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Many readers, one writer - is it possible to avoid locking?

Tags:

Say you have an in-memory list of strings, and a multi-threaded system, with many readers but just one writer thread.

In general, is it possible to implement this kind of system in C#, without using a lock? Would the implementation make any assumptions about how the threads interact (or place restrictions on what they can do, when)?

like image 767
blueberryfields Avatar asked Oct 01 '13 16:10

blueberryfields


People also ask

Do you need to lock when reading?

depends on how you use and read it. if your read is atomic (i.e, won't be interrupted by write) and the read thread does not have dependency with the write threads, then you maybe able to skip read lock. But if your 'read' operation takes some time and takes heavy object interation, then you should lock it for read.

What is a reader/writer lock and when is it useful?

A readers/writer lock regulates access to a set of data. The readers/writer lock is so called because many threads can hold the lock simultaneously for reading, but only one thread can hold the lock for writing. Most device drivers do not use readers/writer locks.

How reader/writer lock differs from a normal lock?

An RW lock allows concurrent access for read-only operations, write operations require exclusive access. This means that multiple threads can read the data in parallel but an exclusive lock is needed for writing or modifying data.

Why do we need read write lock?

In many situations, data is read more often than it is modified or written. In these cases, you can allow threads to read concurrently while holding the lock and allow only one thread to hold the lock when data is modified. A multiple-reader single-writer lock (or read/write lock) does this.


1 Answers

Yes. The trick is to make sure the list remains immutable. The writer will snapshot the main collection, modify the snapshot, and then publish the snapshot to the variable holding the reference to the main collection. The following example demonstrates this.

public class Example
{
  // This is the immutable master collection.
  volatile List<string> collection = new List<string>();

  void Writer()
  {
    var copy = new List<string>(collection); // Snapshot the collection.
    copy.Add("hello world"); // Modify the snapshot.
    collection = copy; // Publish the snapshot.
  }

  void Reader()
  {
    List<string> local = collection; // Acquire a local reference for safe reading.
    if (local.Count > 0)
    {
      DoSomething(local[0]);
    }
  }
}

There are a couple of caveats with this approach.

  • It only works because there is a single writer.
  • Writes are O(n) operations.
  • Different readers may be using different version of the list simultaneously.
  • This is a fairly dangerous trick. There are very specific reasons why volatile was used, why a local reference is acquired on the reader side, etc. If you do not understand these reasons then do not use the pattern. There is too much that can go wrong.
  • The notion that this is thread-safe is semantic. No, it will not throw exceptions, blow up, or tear a whole in spacetime. But, there are other ways in which this pattern can cause problems. Know what the limitations are. This is not a miracle cure for every situation.

Because of the above constraints the scenarios where this would benefit you are quite limited. The biggest problem is that writes require a full copy first so they may be slow. But, if the writes are infrequent then this might be tolerable.

I describe more patterns in my answer here as well including one that is safe for multiple writers.

like image 198
Brian Gideon Avatar answered Oct 10 '22 17:10

Brian Gideon