Другой поток не просыпается даже после уведомления и снятия блокировки в python

import time
from threading import *
import random

class appointment:
    def patient(self):
        for _ in range(2):
            condition_object.acquire()
            print('patient john waiting for appointment')
            condition_object.notify()
            condition_object.wait() # Thread is in waiting state
            print('patiend john successfully got the appointment')
        condition_object.notify()
        condition_object.release()
        print('patient john is done')

    def doctor(self):
        for _ in range(2):
            print('doctor in chamber')
            condition_object.acquire()
            print('doctor jarry checking the time for appointment')
            time=0
            time=random.randint(1,13)
            print('oppointed time is {} PM'.format(time))
            condition_object.notify()
            print('doctor is going to wait')
            condition_object.wait(1.1)
        print('doctor is about to leave')
        condition_object.release()
        print('doctor is done')
    
condition_object = Condition()
class_obj=appointment()

T1 = Thread(target=class_obj.patient)
T2 = Thread(target=class_obj.doctor)

T1.start()
T2.start()

T1.join()
T2.join()
print("\nEND\n")

Это типичный потоковый код производителя-потребителя. И в конце двух циклов я ожидаю, что когда поток пациента уведомит другой поток и снимет блокировку, ожидающий поток врача проснется. Но ожидающий поток доктора не просыпается (даже если я установил таймер).

Поток врача просыпается, если я использую wait() в конце функции пациента() вместо освобождения, но тогда функция пациента не просыпается после того, как я снимаю блокировку с потока врача. Я действительно смущен.

Почему поток доктора не просыпается после снятия блокировки с другого потока?

И

Почему поток доктора просыпается только после вызова функции wait() из другого потока?


person Ritobroto Ganguly    schedule 19.05.2021    source источник


Ответы (2)


Я изменил ваш код следующим образом, и кажется, что он работает как надо:

from threading import *
import random


class Appointment:
    def __init__(self, cond: Condition):
        self.cond = cond

    def patient(self):
        with self.cond:
            for _ in range(2):
                print('Patient John waiting for appointment')
                self.cond.notify()
                self.cond.wait()  # Thread is in waiting state
                print('Patient John successfully got the appointment')
                self.cond.notify()
        print('Patient John is done')

    def doctor(self):
        print('Doctor in chamber')
        with self.cond:
            for _ in range(2):
                print('Doctor Jarry checking the time for appointment')
                t = random.randint(1, 13)
                print('appointed time is {} PM'.format(t))
                self.cond.notify()
                print('Doctor is going to wait')
                self.cond.wait()
            print('Doctor is about to leave')
        print('Doctor is done')


if __name__ == '__main__':
    _condition_object = Condition()
    class_obj = Appointment(_condition_object)
    ths = [Thread(target=class_obj.patient), Thread(target=class_obj.doctor)]
    [th.start() for th in ths]
    [th.join() for th in ths]
    print("\nEND\n")
person Artiom Kozyrev    schedule 20.05.2021
comment
предложение with делает то же самое, что и aquire и release. Правильно? - person Ritobroto Ganguly; 20.05.2021
comment
@RitobrotoGanguly да with - более компактная версия синтаксиса, в ней есть acquire и release под капотом. - person Artiom Kozyrev; 20.05.2021
comment
Таким образом, мой код ig не работал из-за ненужных вызовов метода acquire внутри цикла. Не могли бы вы внести изменения в свой ответ и включить это? - person Ritobroto Ganguly; 20.05.2021
comment
@RitobrotoGanguly Я думаю, у вас была ошибка в отступе после строки `print('пациент Джон успешно получил встречу')` в patient(). Ваш код показался мне сложным для работы, поэтому я переписал его в более компактную форму, что позволяет делать меньше подобных ошибок. - person Artiom Kozyrev; 20.05.2021
comment
Нет, я не думаю, что отступ был проблемой. Тогда мой компилятор выдал бы ошибку. - person Ritobroto Ganguly; 20.05.2021
comment
@RitobrotoGanguly это логическая ошибка, а не синтаксическая, в таком случае ни один компилятор или интерпретатор не выдает ошибку. - person Artiom Kozyrev; 20.05.2021
comment
@RitobrotoGanguly еще одна ошибка, которую я вижу, заключается в том, что вы пытаетесь получить блокировку два раза в patient() - person Artiom Kozyrev; 20.05.2021

Как указано в https://docs.python.org/3/library/threading.html#threading.Condition

Если задан аргумент блокировки, а не None, это должен быть объект Lock или RLock, и он используется в качестве базовой блокировки. В противном случае создается новый объект RLock, который используется в качестве базовой блокировки.

Как указано в https://docs.python.org/3/library/threading.html#threading.RLock

Блокировка с повторным входом должна быть снята тем потоком, который ее захватил. Как только поток получил повторную блокировку, тот же поток может снова получить ее без блокировки; поток должен освобождать его один раз каждый раз, когда он его получает

Итак, проблема с моим кодом заключалась в том, что было больше обращений к acquire RLock, чем к release этому. Следовательно, как показывает https://stackoverflow.com/a/67617044/13023201 этот ответ, используя предложение with с Condition объект или вызов acquire вне цикла решает проблему.

person Ritobroto Ganguly    schedule 20.05.2021