Sobes.tech
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):

  1. Wzajemne wykluczenie (Mutual Exclusion): Zasoby nie mogą być używane jednocześnie przez wiele wątków.
  2. 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.
  3. 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.
  4. 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.