Jak dokładnie działa blokada?


526

Widzę, że w przypadku używania obiektów, które nie są bezpieczne dla wątków, kod zamykamy w taki sposób:

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

Co dzieje się, gdy wiele wątków uzyskuje dostęp do tego samego kodu (załóżmy, że działa on w aplikacji sieci web ASP.NET). Czy są w kolejce? Jeśli tak, jak długo będą czekać?

Jaki wpływ na wydajność ma użycie blokad?


Odpowiedzi:


447

lockWypowiedź jest tłumaczona przez C # 3.0 na następujące kwestie:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

W C # 4.0 to się zmieniło i jest teraz generowane w następujący sposób:

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    // body
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(temp); 
    }
}

Możesz znaleźć więcej informacji o tym, co tuMonitor.Enter robi . Aby zacytować MSDN:

Użyj, Enteraby uzyskać Monitor na obiekcie przekazanym jako parametr. Jeśli inny wątek wykonał Enter obiekt na obiekcie, ale nie wykonał jeszcze odpowiedniego Exit, bieżący wątek będzie blokowany, dopóki inny wątek nie zwolni obiektu. Legalne jest, aby ten sam wątek wywoływał Enterwięcej niż jeden raz bez blokowania; jednak taka sama liczba Exitwywołań musi zostać wywołana, zanim inne wątki oczekujące na obiekt zostaną odblokowane.

Monitor.EnterMetoda będzie czekać w nieskończoność; to się nie skończy.


15
Według MSDN „Korzystanie ze słowa kluczowego lock (C #) lub SyncLock (Visual Basic) jest ogólnie preferowane zamiast bezpośredniego używania klasy Monitor, zarówno dlatego, że lock lub SyncLock jest bardziej zwięzły, a ponieważ lock lub SyncLock zapewnia zwolnienie monitora bazowego, nawet jeśli chroniony kod zgłosi wyjątek. Dokonuje się tego za pomocą słowa kluczowego w końcu, które wykonuje skojarzony blok kodu niezależnie od tego, czy zgłoszony zostanie wyjątek. ” msdn.microsoft.com/en-us/library/ms173179.aspx
Aiden Strydom

10
Jaki jest sens var temp = obj; linia. skoro na początku jest to tylko ref, to po co robić kolejne?
priehl

11
@priehl Umożliwia użytkownikowi zmianę objbez zakleszczenia całego systemu.
Steven

7
@Joymon ostatecznie każda funkcja językowa to cukier składniowy. Funkcje językowe mają na celu zwiększenie produktywności programistów i ułatwienie konserwacji aplikacji, podobnie jak funkcja blokady.
Steven

2
Poprawny. Jest to cały cel funkcji lock-statement i Monitor: abyś mógł wykonać operację w jednym wątku bez martwienia się, że inny wątek go zepsuł.
Dizzy H. Muffin

285

To prostsze niż myślisz.

Według Microsoft : lockSłowo kluczowe zapewnia, że ​​jeden wątek nie wchodzi w krytyczną sekcję kodu, podczas gdy inny wątek znajduje się w sekcji krytycznej. Jeśli inny wątek spróbuje wprowadzić zablokowany kod, zaczeka, zablokuje, aż obiekt zostanie zwolniony.

Słowo lockkluczowe wywołuje Enterna początku bloku i Exitna końcu bloku. locksłowo kluczowe faktycznie obsługuje Monitorklasę z tyłu.

Na przykład:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

W powyższym kodzie najpierw wątek wchodzi do sekcji krytycznej, a następnie zostanie zablokowany obj. Gdy inny wątek próbuje wejść, spróbuje również zablokować obj, który jest już zablokowany przez pierwszy wątek. Drugi wątek będzie musiał poczekać na zwolnienie pierwszego wątku obj. Kiedy pierwszy wątek opuści, kolejny wątek zostanie zablokowany obji przejdzie do sekcji krytycznej.


9
czy powinniśmy stworzyć fikcyjny obiekt do zablokowania, czy możemy zablokować istniejącą zmienną w kontekście?
batmaci,

9
@batmaci - Blokowanie na oddzielnym prywatnym atrapie obiektu daje gwarancję, że nikt inny nie blokuje tego obiektu. Jeśli zablokujesz dane i ten sam fragment danych będzie widoczny na zewnątrz, utracisz tę gwarancję.
Umar Abbas,

8
Co się stanie, jeśli na zwolnienie blokady czeka więcej niż jeden proces? Czy procesy oczekujące są w kolejce, aby zablokować sekcję krytyczną w kolejności FIFO?
jstuardo

@jstuardo - Są w kolejce, ale nie można zagwarantować, że kolejność to FIFO. Sprawdź ten link: albahari.com/threading/part2.aspx
Umar Abbas


47

Nie, nie są w kolejce, śpią

Instrukcja blokady formularza

lock (x) ... 

gdzie x jest wyrażeniem typu odniesienia, jest dokładnie równoważne z

var temp = x;
System.Threading.Monitor.Enter(temp); 
try { ... } 
finally { System.Threading.Monitor.Exit(temp); }

Musisz tylko wiedzieć, że czekają na siebie, a tylko jeden wątek wejdzie, aby zablokować blok, pozostałe zaczekają ...

Monitor jest w pełni napisany w .net, więc jest wystarczająco szybki, spójrz także na klasę Monitor z reflektorem, aby uzyskać więcej szczegółów


6
Zauważ, że kod emitowany dla lockinstrukcji nieznacznie zmienił się w C # 4: blogs.msdn.com/b/ericlippert/archive/2009/03/06/…
LukeH

@ArsenMkrt, nie są trzymane w kolejce w stanie „Zablokowany”. Myślę, że istnieje pewna różnica między stanem uśpienia a stanem bloku, prawda?
Mohanavel,

jaka różnica masz na myśli @Mohanavel?
Arsen Mkrtchyan

1
To nie było pytanie. Pytanie dotyczyło słowa kluczowego „lock”. Załóżmy, że proces wchodzi w sekcję „blokady”. Oznacza to, że proces blokuje ten fragment kodu i żaden inny proces może nie być w stanie wejść do tej sekcji, dopóki blokada nie zostanie zwolniona. Cóż ... teraz 2 kolejne procesy próbują wprowadzić ten sam blok. Ponieważ jest chroniony słowem kluczowym „lock”, będą czekać, zgodnie z tym, co zostało powiedziane na tym forum. Kiedy pierwszy proces zwolni blokadę. Jaki proces wchodzi w blok? pierwszy, który próbował wejść, czy ostatni?
jstuardo

1
Myślę, że masz na myśli wątek zamiast procesu ... jeśli tak, to odpowiedź brzmi Nie, nie ma gwarancji, który wejdzie ... więcej tutaj stackoverflow.com/questions/4228864/...
Arsen Mkrtchyan

29

Zamki będą blokować inne wątki przed wykonaniem kodu zawartego w bloku zamka. Wątki będą musiały poczekać, aż wątek w bloku zamka zostanie zakończony i zamek zostanie zwolniony. Ma to negatywny wpływ na wydajność w środowisku wielowątkowym. Jeśli musisz to zrobić, upewnij się, że kod w bloku blokady może być przetwarzany bardzo szybko. Powinieneś starać się unikać kosztownych działań, takich jak dostęp do bazy danych itp.


11

Wpływ na wydajność zależy od sposobu blokowania. Dobrą listę optymalizacji można znaleźć tutaj: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

Zasadniczo powinieneś spróbować zablokować tak mało, jak to możliwe, ponieważ powoduje to uśpienie kodu oczekiwania. Jeśli masz jakieś ciężkie obliczenia lub trwały kod (np. Przesyłanie pliku) w zamku, powoduje to ogromną utratę wydajności.


1
Ale próba napisania kodu o niskiej blokadzie może często powodować subtelne, trudne do znalezienia i naprawienia błędy, nawet jeśli jesteś ekspertem w tej dziedzinie. Korzystanie z zamka to często mniejsze zło. Powinieneś zablokować dokładnie tyle, ile potrzebujesz, nie więcej, nie mniej!
Łukasza

1
@LukeH: Istnieją pewne wzorce użytkowania, w których kod niskiej blokady może być bardzo prosty i łatwy [ do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue]. Największym niebezpieczeństwem jest to, że jeśli wymagania ewoluują poza to, co takie techniki mogą obsłużyć, dostosowanie kodu do tego może być trudne.
supercat

Link się zepsuł.
CarenRose

8

Część w instrukcji lock może być wykonana tylko przez jeden wątek, więc wszystkie pozostałe wątki będą czekać w nieskończoność na zakończenie wątku zawierającego blokadę. Może to spowodować tzw. Impas.


8

lockWypowiedź jest tłumaczona na wezwania do Enteri ExitmetodMonitor .

lockOświadczenie będzie czekać w nieskończoność na obiekt blokujący ma zostać wydany.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.