Zakleszczenie wątków: lock (lockA) { lock (lockB) { } }

Zakleszczenie, to taka sytuacja gdzie jeden wątek czeka na zakończenie drugiego wątku, aby otrzymać dostęp do sekcji krytycznej, a ten drugi czeka na zakończenie tego pierwszego.

Przeanalizujmy poniższy kod:

class Program
{
    private static readonly object lockA = new object();
    private static readonly object lockB = new object();

    static void Main(string[] args)
    {
        Console.WriteLine("Let's get the started. We are in main thread");
        Task.Factory.StartNew(RiseDeadlock); // #01
        Console.WriteLine("We are before lockB in main thread.");
        lock (lockB) // #04
        {
            Console.WriteLine("We are in lockB in main thread.");
            Console.WriteLine("Simulate long running instruction in main thread.");
            Thread.Sleep(5000); // #05
            Console.WriteLine("The long running instruction has been ended in main thread.");
            Console.WriteLine("We are waiting for release of lockA in main thread.");
            lock (lockA) // #06
            {
                Console.WriteLine("This code will never be executed in main thread.");
            }
        }

        Console.ReadLine();
    }

    private static void RiseDeadlock()
    {
        Console.WriteLine("RiseDeadlock method hes been started in new thread.");
        Console.WriteLine("We are before lockA in RiseDeadlock method.");
        lock (lockA) // #02
        {
            Console.WriteLine("We are in lockA in RiseDeadlock method.");
            Console.WriteLine("Simulate long running instruction in RiseDeadlock method.");
            Thread.Sleep(10000); // #03
            Console.WriteLine("The long running instruction has been ended in RiseDeadlock method.");
            Console.WriteLine("We are in lockA before lockB in RiseDeadlock method.");
            Console.WriteLine("We are waiting for release of lockB in RiseDeadlock method.");
            lock (lockB) // # 07
            {
                Console.WriteLine("This code will never be executed in RiseDeadlock method.");
            }
        }
    }
}

#01

Główny wątek tworzy nowy wątek poboczny, w którym wywołuje metodę RiseDeadlock.

#02

Zakładamy blokadę lockA w metodzie RiseDeadlock. Od tego momentu dostęp do tej sekcji kodu będzie miał tylko jeden wątek. Inne wątki będą czekać na dostęp aż do momentu, gdy aktualny wątek nie wykona wszystkich operacji i nie opuści tej sekcji kodu. Obecnie w tej sekcji krytycznej znajduje się ten nowo utworzony poboczny wątek, powstały na potrzeby wywołania metody RiseDeadlock.

#03

Symulujemy długotrwałe obliczenia, które potrwają około 10 sekund.

#04

Równolegle z wykonywaniem kodu opisanego w punkcie #02 i #03 główny wątek, w metodzie Main założył blokadę lockB. Tutaj analogicznie — jak to jest opisane w punkcie #02 — od teraz dostęp do tej sekcji krytycznej posiada wątek główny. Inne wątki będą czekały na swoją kolej, aż ten wątek opuści tę sekcję kodu.

Obecnie mamy sytuację, w której dwa wątki mają założone swoje blokady. Wątek główny założył blokadę lockB a wątek poboczny założył blokadę lockA.

#05

W głównym wątku, w metodzie Main, znajdując się w sekcji krytycznej lockB również symulujemy długotrwałe obliczenia, które potrwają około 5 sekund.

Symulacja tych długotrwałych obliczeń ma nam zagwarantować to, że wątek poboczny uprzedzi wątek główny i zdąży założyć blokadę lockA, zanim wątek główny założyłby ją tuż po założeniu blokady lockB.

Gdyby wątek główny w metodzie Main zdążył założyć blokadę lockB i lockA, zanim wątek poboczny założyłby blokadę lockA, wówczas nie doszłoby do zakleszczenia. Wątek główny wykonałby swój kod i zwolniłby blokady. Zaraz potem, wątek poboczny otrzymałby dostęp do tych sekcji krytycznych, założyłby swoje blokady lockA i lockB, wykonał swój kod, i również zwolniłby te sekcje krytyczne.

#06

To jeszcze nie zakleszczenie, ale jest bardzo blisko do tego. Wykonała się symulacja długotrwałych obliczeń opisana w punkcie poprzednim. Teraz główny wątek rozpoczyna czekanie aż wątek poboczny zwolni blokadę lockA.

#07

Voilà. Normalnie to nie ma się z czego cieszyć, ale właśnie osiągnęliśmy zakleszczenie. Wątek poboczny zakończył swoje symulacje długotrwałych obliczeń i rozpoczął czekanie na zwolnienie blokady lockB, która została założona przez wątek główny.

Żaden z tych wątków nie doczeka się na zwolnienie tych blokad. Każdy z tych wątków zwolniłby dostęp do sekcji krytycznej, gdyby otrzymał wstęp do kolejnej sekcji, ale nigdy to nie nastąpi. Wątki będą czekać na siebie w nieskończoność. Mamy klasyczne zakleszczenie.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Connecting to %s