Since the ReaderWriterLockSlim
class uses Thread ID to see who owns the lock is it safe to use with async methods where there is no guarentee that all the method will be executed on the same thread.
For example.
System.Threading.ReaderWriterLockSlim readerwriterlock = new System.Threading.ReaderWriterLockSlim(); private async Task Test() { readerwriterlock.EnterWriteLock(); await Task.Yield(); //do work that could yield the task readerwriterlock.ExitWriteLock(); //potentailly exit the lock on a different thread }
Is it Safe to use ReaderWriterLockSlim in an async method
Yes and no. It can be safe to use this in an async method, but it is likely not safe to use it in an async method where you enter and exit the lock spanning an await
.
In this case, no, this is not necessarily safe.
ExitWriteLock must be called from the same thread that called EnterWriteLock
. Otherwise, it throws a SynchronizationLockException
. From the documentation, this exception is thrown when:
The current thread has not entered the lock in write mode.
The only time this would be safe is if this was used in an async method which was always in an environment where there was a current SynchronizationContext
in place which will move things back to the same thread (ie: Windows Forms, WPF, etc), and wasn't used by a nested async call where a "parent" up the call chain setup a Task with ConfigureAwait(false)
(which would prevent the Task
from capturing the synchronization context). If you are in that specific scenario, you'd know the thread would be maintained, as the await
call would marshal you back onto the calling context.
No. Thread-affine coordination primitives should not be used as in your example.
You correctly identified the problem where a different thread can be used to resume after the await
. There is another issue due to the the way async
methods return early: the caller is not aware that the lock is held.
ReaderWriterLockSlim
by default is a non-recursive lock, so if another async
method attempts to take the same lock, you'll get a deadlock. Even if you make the lock recursive, you'll still end up with a problem: arbitrary end-user code should never be called while holding a lock, and that's essentially what you're doing when you use an await
.
The SemaphoreSlim
type is async
-aware (via its WaitAsync
method), and Stephen Toub has a series of async
coordination primitives also available in my AsyncEx library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With