Безопасно ли использовать ReaderWriterLockSlim в асинхронном методе
Так как класс ReaderWriterLockSlim
использует идентификатор потока, чтобы узнать, кому принадлежит блокировка, его безопасно использовать с методами async, где нет никакой гарантии, что весь метод будет выполнен в том же потоке.
Например.
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
}
Ответы
Ответ 1
Можно ли использовать ReaderWriterLockSlim в асинхронном методе
Да и нет. Это может быть безопасно использовать в методе async, но, вероятно, небезопасно использовать его в методе async, где вы вводите и выходите из блокировки, охватывающей await
.
В этом случае нет, это не обязательно безопасно.
ExitWriteLock должен вызываться из того же потока, который называется EnterWriteLock
. В противном случае он выбрасывает SynchronizationLockException
. Из документации это исключение вызывается, если:
Текущий поток не вступил в блокировку в режиме записи.
Единственное время, когда это было бы безопасно, - это то, что это было использовано в методе async, который всегда находился в среде, где был текущий SynchronizationContext
, который будет перемещать вещи обратно в один и тот же поток (то есть: Windows Forms, WPF и т.д.) И не использовался вложенным асинхронным вызовом, где "родительский" вызов цепочки вызовов настраивает задачу с ConfigureAwait(false)
(что предотвратит захват Task
контекста синхронизации). Если вы находитесь в этом конкретном сценарии, вы знаете, что поток будет поддерживаться, так как вызов await
приведет вас к возврату в вызывающий контекст.
Ответ 2
Нет. Нитевидные координационные примитивы не должны использоваться как в вашем примере.
Вы правильно определили проблему, когда другой поток может использоваться для возобновления после await
. Существует еще одна проблема из-за того, как методы async
возвращаются раньше: вызывающий абонент не знает, что блокировка сохранена.
ReaderWriterLockSlim
по умолчанию является нерекурсивной блокировкой, поэтому, если другой метод async
пытается выполнить одну и ту же блокировку, вы получите тупик. Даже если вы сделаете блокировку рекурсивной, вы все равно столкнетесь с проблемой: произвольный код конечного пользователя никогда не должен вызываться, удерживая блокировку, и это, по сути, то, что вы делаете, когда используете await
.
Тип SemaphoreSlim
async
-aware (через метод WaitAsync
), а у Stephen Toub библиотеке AsyncEx.