Найти - Пользователи
Полная версия: Нужна ли блокировка
Начало » Python для новичков » Нужна ли блокировка
1
AlexandrR
Добрый день!
Поясните, правильно ли я использую блокировку (Lock()) в следующем коде или она не требуется:
import threading 

class _ServerThread(threading.Thread):
_lock = None
_daemon = None

def __init__(self):
threading.Thread.__init__(self)
self._lock = threading.Lock()

def run(self):
self._lock.acquire()
self._daemon=Daemon()
self._lock.release()
self._daemon.requestLoop() # Бесконечный цикл внутри

def stop(self):
self._lock.acquire()
self._daemon.shutdown()
self._lock.release()
При этом в особенности реализации Daemon() не вникаем и не знаем, насколько он thread-safe
nerijus
В run не нужен. А в stop, смотря что происходит в shutdown. Но скорее всего тоже не нужен (главное что бы stop вызывался один раз).
AlexandrR
Спасибо!
Не совсем понято только, причем здесь количество вызовов? Меня интересует блокировка как защита от одновременного обращения из разных потоков.
Андрей Светлов
К чему - обращения? Какой объект вы защищаете?
nerijus
AlexandrR
Спасибо!
Не совсем понято только, причем здесь количество вызовов? Меня интересует блокировка как защита от одновременного обращения из разных потоков.
В этом то и дело, что если архитектура кривая, этот stop может быть вызван с двух разных потоков одновременно.
AlexandrR
Андрей Светлов
К чему - обращения? Какой объект вы защищаете?
Защищаю _daemon. Во всяком случае я пытаюсь это сделать, но может этого не нужно делать? Вот это и пытаюсь спросить.

nerijus
В этом то и дело, что если архитектура кривая, этот stop может быть вызван с двух разных потоков одновременно.
Так дело не в “кривой” архитектуре - это сервер, то есть запросы с клиентов идут асинхронно и независимо. Но можно допустить, что stop() будет вызван ровно 1 раз. Но может случиться так, что вызван он будет ровно в тот момент, когда выполнение метода run() в самом начале и тогда _daemon одновременно попытаются изменить два потока. А это же вроде не есть гут?
Андрей Светлов
Создавайте Daemon в конструкторе.
th = _ServerThread()
th.start()
th.stop()
Что можно легко поймать?
th.run еще не начал выполнятся, блокировки нет.
th.stop() захватывает блокировку и вызывает self._daemon.shutdown() для еще не существующего атрибута _daemon. Ахтунг
th.run на этот момент добрался до блокировки. Вы игнорируете try-finally при использовании блоков, исключение из th.stop блокировку не снимает.
th.run ждет доступа к блоку вечно, поток никогда не завершится.

Теперь предположим, что вашему Daemon все же нужно выполнить конструктор в том же потоке, в котором будет крутится его requestLoop. Еще допустим, что _daemon.shutdown() можно вызвать только один раз, второй все поломает (это не всегда верно).

import threading 

class _ServerThread(threading.Thread):
_lock = None
_daemon = None

def __init__(self):
threading.Thread.__init__(self)
self._lock = threading.Lock()
self._condition = threading.Condition(self._lock)
self._started = False
self._stopping = False

def run(self):
try:
self._condition.acquire()
self._daemon=Daemon()
self._started = True
self._condition.notifyAll()
finally:
self._condition.release()
self._daemon.requestLoop() # Бесконечный цикл внутри

def stop(self):
try:
self._condition.acquire()
while not self._started:
self._condition.wait()
if not self._stopping:
self._daemon.shutdown()
self._stopping = True
finally:
self._condition.release()
AlexandrR
Андрей, спасибо!

Очень грамотный и обстоятельный ответ. И как раз именно то, что мне нужно. Все-таки 5 звезд в профиле неспроста.

P.S. Про заворачивание блокировок в try/finally я как-то забыл.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB