2013-04-08 4 views
20

Так как класс ReaderWriterLockSlim использует идентификатор потока, чтобы узнать, кому принадлежит блокировка, безопасно использовать его с помощью методов async, где нет никакой гарантии, что весь метод будет выполняться в том же потоке ,Можно ли использовать ReaderWriterLockSlim в асинхронном методе

Например.

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 
    } 

ответ

21

Является ли это безопасно использовать ReaderWriterLockSlim в методе асинхронного

Да и нет. Это может быть безопасно использовать в методе async, но, вероятно, небезопасно использовать его в методе async, где вы вводите и выходите из блокировки, охватывающей await.

В этом случае нет, это не обязательно безопасно.

ExitWriteLock необходимо вызывать из той же самой нити, которая называется EnterWriteLock. В противном случае он выбрасывает SynchronizationLockException. Из documentation это исключение возникает, когда:

Текущий поток не вступил в режим блокировки в режиме записи.

Единственный раз, когда это будет безопасно, если это было использовано в методе асинхронным, который всегда был в среде, где находился в месте, которое будет двигаться вещи обратно в том же потоке (т.е. ток SynchronizationContext: Windows Forms , WPF и т. Д.) И не был использован вложенным асинхронным вызовом, где «родительский» номер вызывающей цепи настраивает задачу с ConfigureAwait(false) (что предотвратит захват Task контекста синхронизации). Если вы находитесь в этом конкретном сценарии, вы знаете, что поток будет поддерживаться, так как вызов await приведет вас обратно в вызывающий контекст.

+0

Я читаю этот вопрос, и два ответа: ваш и Стивен Клири противоречат друг другу. Я смущен. Я понимаю, почему в WPF, например, блокировка не проблема, потому что это тот же поток, который получил блокировку в первую очередь, но Стивен говорит, несмотря на то, что тот же поток, ReaderWriterLockSlim всегда является проблемой с async, независимо от среды –

+1

@ DonBox Ну, это не «всегда» проблема - она ​​может работать, но это опасно. Я также предлагаю это.Как уже упоминалось, с тем же контекстом вы все равно можете получить проблемы с реентерацией, которые вызовут тупик - если вы знаете, что ваша рутина никогда не будет вызвана> 1 раз повторно, она должна работать, но это большой «если» –

19

№ Нитевидные координационные примитивы не должны использоваться, как в вашем примере.

Вы правильно определили проблему, когда другой поток может использоваться для возобновления после await. Существует еще одна проблема из-за того, как методы async возвращаются раньше: вызывающий абонент не знает, что блокировка сохранена.

ReaderWriterLockSlim по умолчанию является нерекурсивной блокировкой, поэтому, если другой метод async пытается выполнить один и тот же замок, вы получите тупик. Даже если вы сделаете блокировку рекурсивной, вы все равно столкнетесь с проблемой: произвольный код конечного пользователя никогда не должен вызываться, удерживая блокировку, и это по существу то, что вы делаете, когда используете await.

SemaphoreSlim type является async -aware (через его метод WaitAsync), и Стивен Toub имеет series of async coordination primitives также доступны в моем AsyncEx library.

+0

Можете ли вы дайте конкретный пример для 3-го абзаца ", поэтому, если другой метод асинхронизации попытается выполнить одну и ту же блокировку, вы получите тупик". Я не понимаю, как тупик мог появиться в среде, например WPF. –

+1

@DonBox: просто нажмите кнопку, которая берет блокировку записи, а затем «ждет Task.Delay (10000);» и снова щелкните ее до 10 секунд. –

Смежные вопросы