2013-04-08 17 views
20

Ponieważ klasa ReaderWriterLockSlim używa identyfikatora wątku, aby zobaczyć, kto jest właścicielem blokady, jest bezpieczny w użyciu z metodami asynchronicznymi, gdzie nie ma gwarancji, że cała metoda zostanie wykonana w tym samym wątku .Czy można bezpiecznie używać ReadWriterLockSlim w asynchronicznej metodzie

Na przykład.

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 
    } 

Odpowiedz

21

jest to bezpieczne w użyciu ReaderWriterLockSlim w sposób asynchroniczny

tak i nie. Można go bezpiecznie używać w metodzie asynchronicznej, ale prawdopodobnie nie jest bezpieczne używanie go w asynchronicznej metodzie, w której można wchodzić i wychodzić z blokady obejmującej await.

W tym przypadku nie, to niekoniecznie bezpieczne.

ExitWriteLock musi być wywołany z tego samego wątku, który zadzwonił pod numer EnterWriteLock. W przeciwnym razie rzuca SynchronizationLockException. Od documentation ten wyjątek jest generowany, gdy:

Bieżący wątek nie wszedł do trybu zapisu w trybie zapisu.

Jedyny czas ten byłby bezpieczny jest, jeśli ten był używany w sposób asynchroniczny, który był zawsze w środowisku, w którym nie było prądu SynchronizationContext w miejscu, które będzie przenieść rzeczy z powrotem do tego samego wątku (czyli: Windows Forms , WPF itp.) I nie był używany przez zagnieżdżone asynchroniczne wywołanie, w którym "nadrzędny" w górę łańcucha wywołań ustawił zadanie z ConfigureAwait(false) (co uniemożliwiłoby przechwycenie kontekstu synchronizacji Task). Jeśli jesteś w tym konkretnym scenariuszu, wiesz, że wątek zostanie utrzymany, ponieważ wywołanie await przywróci cię do kontekstu wywoływania.

+0

Czytam to pytanie i dwie odpowiedzi, twoje i Stephen Cleary są ze sobą sprzeczne. Jestem zmieszany. Rozumiem, dlaczego w WPF na przykład zamek nie stanowi problemu, ponieważ jest to ten sam wątek, który ma blokadę, ale Stephen mówi, że pomimo tego samego wątku, ReadWriterLockSlim zawsze stanowi problem z asynchronizacją, bez względu na środowisko –

+1

@ DonBox Cóż, to nie jest "zawsze" problem - może działać, ale jest niebezpieczny. Sugeruję to również.Jak wspomniał, w tym samym kontekście nadal możesz napotkać problemy z ponownym wejściem, które spowodują impas - jeśli wiesz, że twoja rutyna nigdy nie zostanie wywołana> 1 raz na nowo, to powinno działać, ale to jest duże "jeśli" –

19

Nie Można używać prymitywów koordynujących wątki afiniczne, jak w twoim przykładzie.

Prawidłowo zidentyfikowano problem polegający na tym, że inny wątek może zostać użyty do wznowienia po await. Jest inny problem ze względu na sposób, w jaki metody zwracają się wcześnie: osoba dzwoniąca nie jest świadoma, że ​​blokada jest wstrzymana.

ReaderWriterLockSlim domyślnie jest nierekurencyjną blokadą, więc jeśli inna metoda async próbuje wykonać tę samą blokadę, dostaniesz zakleszczenie. Nawet jeśli sprawisz, że blokada będzie rekursywna, nadal będziesz mieć problem: dowolny kod użytkownika końcowego nigdy nie powinien być wywoływany podczas trzymania zamka, a to jest zasadniczo to, co robisz, kiedy używasz await.

SemaphoreSlim type jest async -aware (poprzez metody WaitAsync) i Stephen Toub ma series of async coordination primitives również dostępny w moim AsyncEx library.

+0

Czy możesz proszę? daj konkretny przykład do trzeciego akapitu ", więc jeśli inna metoda asynchroniczna spróbuje podjąć tę samą blokadę, dostaniesz zakleszczenie". Nie rozumiem, jak może występować zakleszczenie w środowisku takim jak na przykład WPF. –

+1

@DonBox: Wystarczy kliknąć przycisk, który ma blokadę pisarza, a następnie "poczekaj na Task.Delay (10000);" i kliknij go ponownie przed upływem 10 sekund. –

Powiązane problemy