Junior
Czym jest deadlock?
sobes.tech AI
Odpowiedź od AI
Deadlock to sytuacja bez wyjścia w programowaniu wielowątkowym, gdy dwa lub więcej wątków blokuje się nawzajem, oczekując na zasoby zajęte przez inne wątki z tej samej grupy.
Warunki wystąpienia deadlocka (warunki Coffmana):
- Wzajemne wykluczenie (Mutual Exclusion): Zasoby nie mogą być używane jednocześnie przez wiele wątków.
- Utrzymanie i oczekiwanie (Hold and Wait): Wątki, które już posiadają zasób, oczekują na uzyskanie kolejnego zasobu, zajętego przez inny wątek.
- Brak preempcji (No Preemption): Zasób nie może być wymuszony na wątku, który go posiada; może być zwolniony tylko przez sam wątek.
- Cykliczne oczekiwanie (Circular Wait): Istnieje łańcuch dwóch lub więcej wątków, gdzie każdy oczekuje zasobu, który jest zajęty przez następny w łańcuchu (oczekiwanie w kole).
Przykład prostego deadlocka:
Dwa wątki, dwa zasoby (A i B).
- Wątek 1 przejmuje zasób A.
- Wątek 2 przejmuje zasób B.
- Wątek 1 próbuje przejąć B (który jest zajęty przez wątek 2).
- Wątek 2 próbuje przejąć A (który jest zajęty przez wątek 1).
Oba wątki są zablokowane na zawsze.
// Prosty przykład symulacji deadlocka z użyciem lock
object resourceA = new object();
object resourceB = new object();
void Thread1Method()
{
lock (resourceA) // Wątek 1 przejmuje A
{
Console.WriteLine("Wątek 1 przejął zasób A");
Thread.Sleep(100); // Krótka przerwa dla demonstracji możliwości zmiany wątków
lock (resourceB) // Wątek 1 próbuje przejąć B
{
Console.WriteLine("Wątek 1 przejął zasób B");
}
}
}
void Thread2Method()
{
lock (resourceB) // Wątek 2 przejmuje B
{
Console.WriteLine("Wątek 2 przejął zasób B");
Thread.Sleep(100); // Krótka przerwa
lock (resourceA) // Wątek 2 próbuje przejąć A
{
Console.WriteLine("Wątek 2 przejął zasób A");
}
}
}
// Tworzenie i uruchamianie wątków może prowadzić do deadlocka.
// new Thread(Thread1Method).Start();
// new Thread(Thread2Method).Start();
// Uruchomienie tego kodu może, ale nie musi, pokazać deadlocka,
// ponieważ kolejność wykonania zależy od planisty.
// Jeśli wątek 1 przejmie A, a wątek 2 B, zanim pierwszy
// zdąży przejąć B, powstanie deadlock.
Zapobieganie deadlockom często osiąga się przez:
- Uporządkowanie przejmowania zasobów (zawsze przejmować zasoby w tym samym porządku).
- Używanie metod przejmowania z limitem czasu (próbować przejąć zasób w określonym czasie).
- Projektowanie bez konieczności utrzymywania wielu blokad jednocześnie.
- Używanie operacji atomowych lub wyższych poziomów prymitywów synchronizacji (np.
Monitor.TryEnter,ReaderWriterLockSlim).
Deadlocki są trudne do debugowania, ponieważ zależą od kolejności wykonywania wątków i często pojawiają się tylko pod obciążeniem. Visual Studio oferuje narzędzia do ich wykrywania i analizy.