Вопрос по симуляции тупика в python - PullRequest
2 голосов
/ 20 апреля 2020

Когда я пытался реализовать код python для имитации тупика, я столкнулся с некоторыми интересными вопросами:

1) Я использовал следующий код для симуляции тупика.

  1 from threading import *
  2 import time
  3
  4
  5 def thread_one(lock1, lock2):
  6     print("thread 1 is trying to acquire lock 1")
  7     lock1.acquire()
  8     print("lock1 acquired by thread 1")
  9     time.sleep(1)
 10     print("thread 1 is trying to acquire lock 2")
 11     lock2.acquire()
 12
 13
 14 def thread_two(lock1, lock2):
 15     print("thread 2 is trying to acquire lock 2")
 16     lock2.acquire()
 17     print("lock2 acquired by thread 2")
 18     time.sleep(1)
 19     print("thread 2 is trying to acquire lock 1")
 20     lock1.acquire()
 21
 22
 23 if __name__ == "__main__":
 24     lock1 = Lock()
 25     lock2 = Lock()
 26
 27     t1 = Thread(target=thread_one, args=(lock1, lock2))
 28     t2 = Thread(target=thread_two, args=(lock1, lock2))
 29
 30     t1.start()
 31     t2.start()
 32
 33     t1.join()
 34     t2.join()

И вот мои выводы:

thread 1 is trying to acquire lock 1
lock1 acquired by thread 1
thread 2 is trying to acquire lock 2
lock2 acquired by thread 2
thread 1 is trying to acquire lock 2
thread 2 is trying to acquire lock 1
(program stuck here)

Пожалуйста, поправьте меня, если я ошибаюсь. Я думаю, что моя симуляция правильная, и два потока застряли на этапе получения 2-го замка.

2) Затем я внес следующие изменения:

 10     print("thread 1 is trying to release lock 2")
 11     lock2.release()

 28     t2 = Thread(target=thread_one, args=(lock1, lock2))

В принципе, я хочу оба экземпляры потоков для запуска той же функции thread_one и в функции tr ie освобождают lock2, который еще не был получен. Затем я получил следующие результаты:

thread 1 is trying to acquire lock 1
lock1 acquired by thread 1
thread 1 is trying to acquire lock 1
thread 1 is trying to release lock 2
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/Users/bawang/.pyenv/versions/3.6.5/lib/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "/Users/bawang/.pyenv/versions/3.6.5/lib/python3.6/threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "test.py", line 11, in thread_one
    lock2.release()
RuntimeError: release unlocked lock
(program stuck here)

Мой вопрос: почему оба потока там зависают (мне нужно дважды нажать ctrl + c, чтобы отменить оба из них)? Я понимаю, что второй поток ожидает освобождения lock1 первым потоком. Однако почему первый поток застревает после создания исключения?

3) Затем я продвинулся немного дальше, внеся следующие изменения:

  5 def thread_one(lock1, lock2):
  6     print("thread 1 is trying to acquire lock 1")
  7     lock1.acquire()
  8     print("lock1 acquired by thread 1")
  9     time.sleep(1)
 10     print("thread 1 is trying to release lock 2")
 11     lock2.release()
 12
 13
 14 def thread_two(lock1, lock2):
 15     print("thread 2 is trying to acquire lock 2")
 16     lock2.acquire()
 17     print("lock2 acquired by thread 2")
 18     time.sleep(1)
 19     print("thread 2 is trying to release lock 1")
 20     lock1.release()

 27     t1 = Thread(target=thread_one, args=(lock1, lock2))
 28     t2 = Thread(target=thread_two, args=(lock1, lock2))

На этот раз я хочу посмотреть, что произойдет, если lock1 & lock2 будут получены одним потоком при освобождении в другом. Вот мои выводы:

thread 1 is trying to acquire lock 1
lock1 acquired by thread 1
thread 2 is trying to acquire lock 2
lock2 acquired by thread 2
thread 1 is trying to release lock 2
thread 2 is trying to release lock 1
(Program completes)

Мой вопрос: почему нет исключений? Я ожидал двух RuntimeError: в этом случае снимите разблокированную блокировку .

Ответы [ 2 ]

2 голосов
/ 20 апреля 2020

1) Да. Вы правы.

2) Как вы уже догадались, 1-й поток остановился, но у вас есть еще два потока: 2-й поток и основной поток. Первый cntl + c убивает основной поток. Вы можете проверить сообщение для KeybboardInterrupt. Первое происходит в t2.join().

3) Оба потока получены и освобождены правильно. Различные потоки могут получить один и тот же замок. Поэтому поток 1 просто снял блокировку 2, которая была получена потоком 2, и наоборот.

1 голос
/ 20 апреля 2020

Не существует гарантированного порядка выполнения вашего кода, поэтому поток 1 может получить освобождение блокировки 2, прежде чем поток 2 сможет его получить. Но вы можете запустить код снова, и поток 2 может получить его до того, как поток 1 затем освободит его. Я не уверен, чего вы пытались достичь в примере на полпути.

Причина, по которой в последнем примере не генерируются какие-либо исключения, должна быть ясна из вывода:

thread 1 is trying to acquire lock 1
lock1 acquired by thread 1
thread 2 is trying to acquire lock 2
lock2 acquired by thread 2
thread 1 is trying to release lock 2
thread 2 is trying to release lock 1

Все, что пытаются сделать потоки, им удается. Поскольку вы передали одинаковые блокировки обоим потокам , нет никаких причин, по которым поток 1 не может получить блокировку 1, а затем поток 2 снимает ее, в то время как поток 2 получает блокировку 2, а затем поток 1 снимает ее - программа может завершить успешно.

Из-за sleep(1) вы почти гарантированно получите этот вывод, хотя и не совсем - если что-то помешало запуску потока 2 в течение секунды, поток 1 мог бы все же попытаться снять блокировку 2 до того, как он приобретен - рассчитывать на тайм-ауты не является безопасным способом кодирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...