'Dispose ReaderWriterLockSlim after ExitWriterLock
I have a function to clean up some objects as well as the ReaderWriterLockSlim. But I need the ReaderWriterLockSlim to lock as writer lock to prevent the other thread read the data while I am doing the clean up.
ConcurrentDictionary<string, ReaderWriterLockSlim> RwLocks = new ConcurrentDictionary<string, ReaderWriterLockSlim>();
private ReaderWriterLockSlim GetRwLock(string key)
{
return RwLocks.GetOrAdd(key, _ => new ReaderWriterLockSlim());
}
public void CleanUp(string key)
{
ReaderWriterLockSlim rwLock = this.GetRwLock(key);
try
{
rwLock.EnterWriterLock();
// do some other clean up
this.RwLocks.TryRemove(key, out _);
}
finally
{
rwLock.ExitWriterLock();
// It is safe to do dispose here?
// could other thread enter the read lock or writer lock here?
// and the dispose will throw exceptions?
// What is the best practice to do the dispose?
rwLock.Dispose();
}
}
I have an idea to wrap the ReaderWriterLockSlim
. Do you think it could solve the problem or have any potential risk
public class ReaderWriterLockSlimWrapper
{
private ReaderWriterLockSlim rwLock;
private volatile bool disposed;
public ReaderWriterLockSlimWrapper()
{
rwLock = new ReaderWriterLockSlim();
disposed = false;
}
private void DisposeInternal()
{
if (!rwLock.IsReadLockHeld && !rwLock.IsUpgradeableReadLockHeld && !rwLock.IsWriteLockHeld)
{
rwLock.Dispose();
}
}
public void Dispose()
{
disposed = true;
DisposeInternal();
}
public void EnterReadLock()
{
rwLock.EnterReadLock();
}
public void ExitReadLock()
{
rwLock.ExitReadLock();
if (disposed)
{
DisposeInternal();
}
}
public void EnterWriteLock()
{
rwLock.EnterWriteLock();
}
public void ExitWriteLock()
{
rwLock.ExitWriteLock();
if (disposed)
{
DisposeInternal();
}
}
}
Solution 1:[1]
You haven't described the specific scenario where you intend to use your two mechanisms, neither for the first one with the CleanUp
/GetRwLock
methods, nor for the second one with the ReaderWriterLockSlimWrapper
class. So I guess the question is:
Are my two mechanisms safe to use in all possible multithreaded scenarios, where thread-safety and atomicity of operations is mandatory?
The answer is no, both of your mechanisms are riddled with race conditions, and offer no guarantees about atomicity. Using them in a multithreaded scenario would result in undefined behavior, including but not limited to:
- Unexpected exceptions.
- Violations of the policies that a correctly used
ReaderWriterLockSlim
is normally expected to enforce. In order words it is possible that two threads will acquire a writer lock for the same key concurrently to each other, or concurrently with others threads that have acquired a reader lock for the same key, or both.
Explaining why your mechanisms are flawed is quite involved. A general explanation is that whenever you use the pattern if (x.BooleanProperty) x.Method();
in a multithreaded environment, although the BooleanProperty
and the Method
might be individually thread-safe, you are allowing a second thread to preempt the current thread between the two invocations, and invalidate the result of the first check.
As a side note, be aware that the ReaderWriterLockSlim
is not a cross-process synchronization primitive. So even if you fix your mechanisms and then attempt to use them in a web application, the policies might still be violated because the web server might decide at random moments to recycle the current process and start a new one. In that case the web application might by running concurrently on two processes, for a period of time that spans a few seconds or even minutes.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Theodor Zoulias |