Страницы

Поиск по вопросам

понедельник, 3 февраля 2020 г.

Порядок запуска потоков

#c_sharp #многопоточность


Всем привет. Подскажите пожалуйста почему идет запуск методов в потоках именно в
таком порядке. Вот код:

using System;
using System.Threading;
class a 
{
    object o = new object();
    public void Test()
    {
        lock (o)
        {
            Console.WriteLine("Start");
            Monitor.Wait(o);
            Console.WriteLine("Stop");
        }
    }
    public void UnLock()
    {
        lock(o)
        Monitor.Pulse(o);
        Console.WriteLine("Unlock");
        for(int x = 0; x <10; x++)
        {
            Thread.Sleep(500);
            Console.WriteLine(x);
        }
    }
}
class b
{
    static void Main()
    {
        a A = new a();
        Thread t = new Thread(A.Test);
        t.Start();
        Thread t1 = new Thread(A.UnLock);
        t1.Start();
    }
}


Я ожидаю что вначале запустится Test, потом написав слово Start он останавливается,
потом запускается метод UnLock и при вызове в нем Pulse() метод Test не должен продолжать
свое дело, так как Pulse() не снимает блокировку с объекта, однако как только вызывается
Pulse(), то несмотря на заблокированный объект в методе Unlock, метод Test начинает
продолжать свое дело. Почему так?? То есть я ожидаю вывод такой 

Start
Unlock
0....9
Stop


а получается

Start
Unlock
Stop
0....9


То есть видно, что метод Pulse говорит о освобождении объекта блокировки, и не смотря
на то что он не освободился по факту, метод Test продолжает свою работу
    


Ответы

Ответ 1



У вас в коде две проблемы. Первая - неправильный отступ. У вас сейчас на самом деле написано следующее: public void UnLock() { lock(o) { Monitor.Pulse(o); } Console.WriteLine("Unlock"); for(int x = 0; x <10; x++) { Thread.Sleep(500); Console.WriteLine(x); } } как указал Pavel Mayorov, вы освобождаете лок сразу же после вызова Pulse. Вторая - само предположение, что второй поток будет запущен позже. Это многопоточность, и нет никаких гарантий что Test начнет выполнятся до UnLock. Даже починив lock, при определенном положении звезд, вы вполне можете получить вывод: Unlock 0....9 Start и зависание на Wait.

Ответ 2



Сразу после вызова Pulse вы освобождаете блокировку, выходя из блока lock(o) - вот первый поток и продолжает работу.

Ответ 3



Из документации: Поток, который в данный момент владеет блокировкой указанного объекта, вызывает этот метод для сообщения следующему в очереди потоку о блокировке. При получении импульса ожидающий поток перемещается в очередь готовности. Когда поток, вызвавший метод Pulse, освобождает блокировку, следующий поток в очереди готовности (который необязательно является потоком, получившим импульс) получает блокировку. Т.е. поскольку вы берете лок только на вызов метода Pulse(), то Test() продолжает работу сразу после того, как освобождается блокировка в Unlock(). Чтобы исправить это, блокировку надо брать на весь код: public void UnLock() { lock(o) { Monitor.Pulse(o); Console.WriteLine("Unlock"); for(int x = 0; x <10; x++) { Thread.Sleep(500); Console.WriteLine(x); } } } (И логичнее будет вызывать Pulse() после цикла.) Но у вас есть еще одно неверное предположение: что метод Test() будет запущен раньше, чем Unlock(). Это необязательно так. Первым может запуститься метод Unlock().

Комментариев нет:

Отправить комментарий