0

In my Application I have various threads reading data from collections. Say, there are 20 readers. One Thread is responsible for periodically refreshing that collection with new data.

Now, using the lock statement from within all threads (reader and writer) will have the drawback, that the readers block each other as well. Simplified Example:

private readonly object lockObj = new Object();
private List<String>() data;

public void dataReader1(){
   lock(lockObj){
      //read...
   }
}

public void dataReader2(){
   lock(lockObj){
      //read...
   }
}

public void dataReader3(){
   lock(lockObj){
      //read...
   }
}

public void dataWriter(){
   lock(lockObj){
      //write write write...
   }
}

Obviously now only one reader can access the data at the time.

To overcome this issue, I could use a global writeLock and different read lock objects like this:

private  List<String>() data;

private readonly object writeLock = new Object();
private readonly object readLock1 = new Object();
private readonly object readLock2 = new Object();
private readonly object readLock3 = new Object();

public void dataReader1(){
   lock(readLock1){
      //read...
   }
}

public void dataReader2(){
   lock(readLock2){
      //read...
   }
}

public void dataReader3(){
   lock(readLock3){
      //read...
   }
}

public void dataWriter(){
   lock(readLock1){
      lock(readLock2){
         lock(readLock3){
             lock(writeLock){
                //write write write...
             }
          }
      }
   }
}

Now, every thread can read - until the writer starts to lock them out, till he finally got the exclusive write Lock.

I don't really like this approach, because nesting locks smells like dead-lock incoming sooner or later. So, I wonder if there is a native way to achieve this with a single lock statement?

Thinking of something like ...

private readonly object lockObj= new Object();
private List<String>() data;

public void dataReader1(){
   readonly lock(lockObj){
      //read...
   }
}

public void dataReader2(){
   readonly lock(lockObj){
      //read...
   }
}

public void dataReader3(){
   readonly lock(lockObj){
      //read...
   }
}

public void dataWriter(){
   lock(lockObj){
      //write write write...
   }
}

... where C# is able to understand, that readonly lock can be entered, unless there is a lock in place (or trying to be aquired) - and that lock in turn is waiting, until there is no more existing readonly lock.

dognose
  • 20,360
  • 9
  • 61
  • 107

1 Answers1

1

It seems, that you are looking for ReaderWriterLockSlim, e.g.

public class MyClass : IDisposable {
  private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

  // Same for DataReader2, DataReader3 etc. 
  public void DataReader1() {
    _lock.EnterReadLock();

    try { // Relevant Code here
    }
    finally {
      _lock.ExitReadLock();
    }
  }

  public void DataWriter() {
    _lock.EnterWriteLock();

    try { // Relevant Code here
    }
    finally {
      _lock.ExitWriteLock();
    }
  }

  public void Dispose() => Dispose(true);

  protected virtual void Dispose(bool disposing) {
    if (disposing) {
      _lock.Dispose(); 
    }
  }
}

So several readers can share the resource, but writer has the exclusive access.

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215