Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between EnterUpgradeableReadLock and EnterReadLock?

When I read the C# Multithreading, I'm confused about what is the difference between EnterReadLock and EnterUpgradeableReadLock In the code below, can replace cacheLock.EnterUpgradeableReadLock() with cacheLock.EnterReadLock()?

private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
private Dictionary<int, string> innerCache = new Dictionary<int, string>();
public AddOrUpdateStatus AddOrUpdate(int key, string value)
{
    //can there be cacheLoack.EnterReadLock?
    cacheLock.EnterUpgradeableReadLock();
    try
    {
        string result = null;
        if (innerCache.TryGetValue(key, out result))
        {
            if (result == value)
            {
                return AddOrUpdateStatus.Unchanged;
            }
            else
            {
                cacheLock.EnterWriteLock();
                try
                {
                    innerCache[key] = value;
                }
                finally
                {
                    cacheLock.ExitWriteLock();
                }
                return AddOrUpdateStatus.Updated;
            }
        }
        else
        {
            cacheLock.EnterWriteLock();
            try
            {
                innerCache.Add(key, value);
            }
            finally
            {
                cacheLock.ExitWriteLock();
            }
            return AddOrUpdateStatus.Added;
        }
    }
    finally
    {
        cacheLock.ExitUpgradeableReadLock();
    }
}
like image 359
Hotkang Avatar asked Mar 04 '26 12:03

Hotkang


1 Answers

If you want to upgrade to a write lock it must be an upgradeable lock.

Why not just always use an upgradeable lock? What's the downside? This:

Only one thread can enter upgradeable mode at any given time. (https://learn.microsoft.com/en-us/dotnet/api/system.threading.readerwriterlockslim.enterupgradeablereadlock?view=netframework-4.8)

This is a critical problem in your code. Since you are always taking the lock in this mode even when just reading you effectively have single-threaded access to the data structure. Effectively, it's no longer a reader writer lock at all.

I understand it is your intention to allow concurrency in the common read case while achieving safety in the write case. Maybe the easiest fix is to first try reading from the cache in read mode. If a write turns out to be required exit the lock completely and retry in upgradeable mode or directly in write mode. That way the fast path has concurrency.

That said I wonder what kinds of real world scalability ReaderWriterLockSlim has. Your locked code region is very cheap. The internal data structures of the lock might become contended and all scalability might be destroyed. It might end up being just as fast or slower than Monitor/lock.

ConcurrentDictionary is designed to scale out across keys. This is probably the way to go.

like image 151
usr Avatar answered Mar 07 '26 02:03

usr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!