W aplikacji do obsługi wątków ac #, jeśli miałbym zablokować obiekt, powiedzmy, że jest to kolejka, a jeśli wystąpi wyjątek, czy obiekt pozostanie zablokowany? Oto pseudokod:
int ii;
lock(MyQueue)
{
MyClass LclClass = (MyClass)MyQueue.Dequeue();
try
{
ii = int.parse(LclClass.SomeString);
}
catch
{
MessageBox.Show("Error parsing string");
}
}
Jak rozumiem, kod po haczyku nie jest wykonywany - ale zastanawiałem się, czy blokada zostanie zwolniona.
Odpowiedzi:
Pierwszy; czy rozważałeś TryParse?
in li; if(int.TryParse(LclClass.SomeString, out li)) { // li is now assigned } else { // input string is dodgy }
Zamek zostanie zwolniony z 2 powodów; po pierwsze,
lock
to zasadniczo:Monitor.Enter(lockObj); try { // ... } finally { Monitor.Exit(lockObj); }
Druga; łapiesz i nie rzucasz ponownie wewnętrznego wyjątku, więc tak
lock
naprawdę nigdy nie widzi wyjątku. Oczywiście blokujesz się na czas trwania MessageBox, co może być problemem.Tak więc zostanie uwolniony we wszystkich, oprócz najbardziej fatalnych, katastrofalnych, niemożliwych do odzyskania wyjątków.
źródło
TheadAbortException
wystąpi międzyMonitor.Enter
atry
: blogs.msdn.com/ericlippert/archive/2009/03/06/ ...Zwracam uwagę, że nikt nie wspomniał w swoich odpowiedziach na to stare pytanie, że zwolnienie blokady w przypadku wyjątku jest niezwykle niebezpieczną rzeczą. Tak, instrukcje blokujące w języku C # mają semantykę „w końcu”; kiedy sterowanie wychodzi z blokady normalnie lub nienormalnie, blokada zostaje zwolniona. Wszyscy mówicie o tym, jakby to było dobre, ale to jest złe! Właściwą rzeczą do zrobienia, jeśli masz zablokowany region, który zgłasza nieobsługiwany wyjątek, jest przerwanie chorego procesu bezpośrednio przed zniszczeniem większej ilości danych użytkownika , a nie zwolnienie blokady i kontynuowanie .
Spójrz na to w ten sposób: przypuśćmy, że masz łazienkę z zamkiem w drzwiach i kolejką ludzi czekających na zewnątrz. Bomba w łazience wybucha, zabijając osobę w środku. Twoje pytanie brzmi: „czy w takiej sytuacji zamek zostanie automatycznie odblokowany, aby następna osoba mogła wejść do łazienki?” Tak, to będzie. To nie jest dobra rzecz. Właśnie tam wybuchła bomba i kogoś zabiła! Prawdopodobnie kanalizacja jest zniszczona, dom nie jest już w dobrym stanie konstrukcyjnym i może być tam kolejna bomba . Należy jak najszybciej wyprowadzić wszystkich i zburzyć cały dom.
To znaczy, przemyśl to: jeśli zablokowałeś region kodu, aby odczytać ze struktury danych bez mutacji w innym wątku, a coś w tej strukturze danych rzuciło wyjątek, szanse są dobre, ponieważ struktura danych jest uszkodzony . Dane użytkownika są teraz pomieszane; nie chcesz w tym momencie próbować zapisywać danych użytkownika, ponieważ zapisujesz uszkodzone dane. Po prostu zakończ proces.
Jeśli zablokowałeś region kodu w celu wykonania mutacji bez kolejnego wątku odczytującego stan w tym samym czasie, a mutacja wyrzuca, to jeśli dane nie były wcześniej uszkodzone, na pewno tak jest teraz . Przed którym dokładnie scenariuszem ma chronić zamek . Teraz kod, który czeka na odczytanie tego stanu, natychmiast uzyska dostęp do uszkodzonego stanu i prawdopodobnie sam się zawiesi. Ponownie, właściwą rzeczą jest zakończenie procesu.
Bez względu na to, jak go pokroisz, wyjątek w zamku to zła wiadomość . Właściwe pytanie nie brzmi: „czy mój zamek zostanie wyczyszczony w przypadku wyjątku?” Właściwe pytanie, które należy zadać, brzmi: „w jaki sposób mogę upewnić się, że wewnątrz blokady nigdy nie ma wyjątku? A jeśli tak, to w jaki sposób ustrukturyzować mój program, aby mutacje zostały przywrócone do poprzedniego dobrego stanu?”
źródło
tak, to wyda się prawidłowo;
lock
działa jaktry
/finally
, zMonitor.Exit(myLock)
w końcu, więc bez względu na to, jak wyjdziesz, zostanie zwolniony. Na marginesie,catch(... e) {throw e;}
najlepiej tego unikać, ponieważ uszkadza to ślad stosue
; lepiej go w ogóle nie łapać lub alternatywnie: używaćthrow;
zamiastthrow e;
który rzuca ponownie.Jeśli naprawdę chcesz wiedzieć, blokada w C # 4 / .NET 4 to:
{ bool haveLock = false; try { Monitor.Enter(myLock, ref haveLock); } finally { if(haveLock) Monitor.Exit(myLock); } }
źródło
„Instrukcja lock jest kompilowana do wywołania Monitor.Enter, a następnie blokuje try… last. W ostatnim bloku jest wywoływana Monitor.Exit.
Generowanie kodu JIT zarówno dla x86, jak i x64 zapewnia, że nie może wystąpić przerwanie wątku między wywołaniem Monitor.Enter a blokiem try, który następuje bezpośrednio po nim. "
Zaczerpnięte z: tej witryny
źródło
Monitor.Enter
atry
, tak że warunek „natychmiast następuje” w JIT jest naruszony.Twój zamek zostanie prawidłowo zwolniony. A
lock
działa tak:try { Monitor.Enter(myLock); // ... } finally { Monitor.Exit(myLock); }
A
finally
bloki są na pewno wykonywane, niezależnie od tego, jak opuścisztry
blok.źródło
Żeby dodać trochę do doskonałej odpowiedzi Marca.
Takie sytuacje są przyczyną istnienia
lock
słowa kluczowego. Pomaga programistom upewnić się, że blokada została zwolniona wfinally
bloku.Jeśli jesteś zmuszony używać
Monitor.Enter
/Exit
np. Do obsługi limitu czasu, musisz upewnić się, że wywołanie jest umieszczoneMonitor.Exit
wfinally
bloku, aby zapewnić prawidłowe zwolnienie blokady w przypadku wyjątku.źródło