2

I recently had to use ReaderWriterLockSlim to synchronize access to several resources that are shared between multiple threads. While doing it, I felt that using ReaderWriterLockSlim is not easy specially when you have to use it in multiple places. You have to have try...finally blocks and remember to open and close the locks. In many cases I've also found myself opening a write lock and closing a read lock instead of closing the write lock. Therefore I tried to come up with an easier way of using the ReaderWriterLockSlim. This is where I got

class Locked<T>
{
    private T _resource;
    private ReaderWriterLockSlim _lock;

    public Locked(T resource)
    {
        _resource = resource;
        _lock = new ReaderWriterLockSlim();
    }

    public void Read(Action<T> ReadAction)
    {
        try
        {
            _lock.EnterReadLock();
            ReadAction(_resource);
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public void Write(Action<T> WriteAction)
    {
        try
        {
            _lock.EnterWriteLock();
            WriteAction(_resource);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }
}

Now for e.g. if we need to synchronize access to a List<string> this is how we do it using the class above

public class Demo
{
    private Locked<List<string>> _listOfString;

    public Demo()
    {
        _listOfString = new Locked<List<string>>(new List<string>());
    }

    public void writeMethod(string value)
    {
        _listOfString.Write(list =>
        {
            list.Add(value);
        });
    }

    public string readMethod(int index)
    {
        string value = null;
        _listOfString.Read(list =>
        {
            value = list[index];
        });
        return value;
    }
}

Do you think this approach is any better? Are there any shortcomings or flaws.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
Ali Kazmi
  • 3,610
  • 6
  • 35
  • 51

1 Answers1

4

I would rather wrap the locking logic in an IDisposable and put the code you want to lock in a using block, very similar to the lock statement:

class DisposableLock : IDisposable
{
    Action _exitLock;
    public DisposableLock(Action exitLock)
    {
        _exitLock = exitLock;
    }

    public void Dispose()
    {
        _exitLock();
    }
}

class ReadLock : DisposableLock
{
    public ReadLock(ReaderWriterLockSlim slimLock)
        : base(() => slimLock.ExitReadLock())
    {
        slimLock.EnterReadLock();
    }
}

class WriteLock : DisposableLock
{
    public WriteLock(ReaderWriterLockSlim slimLock)
        : base(() => slimLock.ExitWriteLock())
    {
        slimLock.EnterWriteLock();
    }
}

You would use it like this:

using(new ReadLock(_lock))
{
    // ... your synchronized operation
}

using(new WriteLock(_lock))
{
    // ... your synchronized operation
}
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • What advantages does doing this have over the approach I took? – Ali Kazmi Jul 21 '11 at 06:40
  • 1
    IMHO it looks cleaner and it looks similar to the lock statement everyone is used to. Additionally, you don't need to change the types of your variables. – Daniel Hilgarth Jul 21 '11 at 06:44
  • I think it depends on your situation: If you have a lot of lists and the access to each one of them needs to be synchronized, but the lists should be independent, your approach is better, because you directly have one `ReaderWriterLockSlim` per list. In my approach, you would need to have to create an additional `ReaderWriterLockSlim` for each list. – Daniel Hilgarth Jul 21 '11 at 06:47
  • However, if you have a lot of code that should all be locked by the same lock, my approach is better, because it doesn't restrict you to one type. – Daniel Hilgarth Jul 21 '11 at 06:48