Как правильно использовать поток обратного отсчета, как я могу остановить его преждевременно?

Threading работает не так, как я ожидаю.

У меня есть рабочее решение, где я контролирую, когда холодильник открывается и закрывается с помощью малины Pi и язычкового переключателя (воспроизведение звука неоспоримо и приостановлено). Теперь я хотел добавить таймер, чтобы что-то сделать, когда дверь слишком долго открывалась. Я решил начать поток, который будет спать в течение x секунд, прежде чем действие предупреждения будет хорошей идеей. Я бы убил нить сигналом, когда переключатель снова закрыт.

Мой подход терпит неудачу. Запуск потока CountDown запускается, но команда завершения сигнала запуска выполняется, но не имеет никакого эффекта. Кроме того, команды, следующие за c.terminate() , не выполняются. Я посмотрел примеры для потокования, но они, похоже, для более сложных ситуаций. Что мне не хватает?

Код:

 #!/usr/bin/env python2.7 import threading, subprocess, sys, time, syslog import RPi.GPIO as GPIO sound = "/home/pi/sounds/fridge_link.mp3" # sound to play while switch is open cmd = ['mplayer', '-nolirc', '-noconsolecontrols', '-slave', '-quiet', sound] # command to play sound lim = 10 # seconds until warning # thread for countdown (should be interruptable) # based on http://chimera.labs.oreilly.com/books/1230000000393/ch12.html#_solution_197 class CountdownTask: def __init__(self): self._running = True def terminate(self): self._running = False print("thread killed") def run(self, n): print("start timer") time.sleep(n) ## action when timer isup print("timer ended") c = CountdownTask() t = threading.Thread(target=c.run, args=(lim,)) t.daemon = True p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE) p.stdin.write('\npausing_keep pause\n') REED = 27 # data pin of reed sensor (in) # GPIO setup GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(REED,GPIO.IN, pull_up_down=GPIO.PUD_DOWN) def edge(channel): if GPIO.input(REED): print("detect close") c.terminate() p.stdin.write('\npause\n') pass else: print("detect open") t.start() p.stdin.write('\npausing_toggle pause\n') def main(): GPIO.add_event_detect(REED, GPIO.BOTH,callback=edge,bouncetime=1000) while True: time.sleep(0.2) pass #------------------------------------------------------------ if __name__ == "__main__": main() 

Новая версия:

 #!/usr/bin/env python2.7 import threading, subprocess, sys, time, syslog import RPi.GPIO as GPIO sound = "/home/pi/sounds/fridge_link.mp3" # sound to play while switch is open cmd = ['mplayer', '-nolirc', '-noconsolecontrols', '-slave', '-quiet', sound] # command to play sound lim = 10 # seconds until warning # thread for countdown (should be interruptable) class CountdownTask: global dooropen def __init__(self): self._running = True def terminate(self): self._running = False print("thread killed") def run(self, n): while self._running and dooropen == False: time.sleep(0.2) pass while self._running and dooropen: print("start timer") time.sleep(n) ## action when timer isup print("timer ended") c = CountdownTask() t = threading.Thread(target=c.run, args=(lim,)) t.daemon = True p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE) p.stdin.write('\npausing_keep pause\n') REED = 27 # data pin of reed sensor (in) # GPIO setup GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(REED,GPIO.IN, pull_up_down=GPIO.PUD_DOWN) dooropen = False # assuming door's closed when starting def edge(channel): global dooropen if GPIO.input(REED): # * no longer reached if dooropen == False: # catch fridge compressor spike print("false close alert") return p.stdin.write('\npause\n') dooropen = False pass else: print("detect open") if dooropen == True: print("false open alert") return p.stdin.write('\npausing_toggle pause\n') dooropen = True def main(): GPIO.add_event_detect(REED, GPIO.BOTH,callback=edge,bouncetime=1000) t.start() while True: time.sleep(0.2) pass #------------------------------------------------------------ if __name__ == "__main__": main() 

Скорректированный раздел, работающий сейчас:

  def run(self, n): while self._running and dooropen == False: time.sleep(0.2) pass while self._running and dooropen: time.sleep(n) if dooropen: ## action when timer isup 

One Solution collect form web for “Как правильно использовать поток обратного отсчета, как я могу остановить его преждевременно?”

Ваш запрограммированный механизм завершения потока через self._running не работает, потому что вы не проверяете состояние self._running в self._running run() протектора (что фактически выполняется в примере, на который вы ссылались).

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

 import threading import time dooropen = True def warnafter(timeout): time.sleep(timeout) if dooropen: print("Warning!") t = threading.Thread(target=warnafter, args=(2,)) t.start() time.sleep(1) dooropen = False t.join() - import threading import time dooropen = True def warnafter(timeout): time.sleep(timeout) if dooropen: print("Warning!") t = threading.Thread(target=warnafter, args=(2,)) t.start() time.sleep(1) dooropen = False t.join() 

Измените time.sleep(1) на time.sleep(3) и time.sleep(3) предупреждение. Почему это работает и как это переводится в ваш прецедент?

Прежде всего, давайте дадим имена вещей. У вас есть свой основной поток и «предупреждающий поток». Это краеугольные камни архитектуры в моем примере кода:

  • У вас есть общее состояние между двумя потоками, указывающее, открыта ли дверь или нет, что означает, следует ли выпустить предупреждение или нет. Назовем это государственным dooropen , и это может быть True или False . Это переменная, доступная как в области основного потока, так и в области, к которой имеет доступ поток уведомлений. То есть, он живет в общей памяти.

  • Это ваше соглашение: dooropen записывается только из основного потока. Предупреждающий поток только читает его.

  • Создайте свой поток предупреждений, когда вы считаете, что это подходящее время. Заставьте его спать (точное время сна может быть ненадежным, особенно на встроенных системах).

  • Важнейшая часть: прямо перед тем, как поднимать тревогу в потоке предупреждения, проверьте dooropen состояние dooropen . Если not dooropen , просто не поднимайте будильник!

Вы видите две разные парадигмы?

Ваша парадигма заключается в том, чтобы установить вооруженную бомбу, запрограммированную на взорваться через определенное время. Эта бомба больше не разговаривает с тобой. Ваша надежда состоит в том, что вы можете обезвредить / уничтожить бомбу, прежде чем она сможет взорваться, если вам не понадобится ее больше взорваться.

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

Учитывая последнюю парадигму, если сообщается, что поток предупреждений не выполняет свое действие, он молча закрывается сам по себе. Понятие «прекращение потока извне» не требуется!

На практике вам понадобится немного более продвинутая концепция, в которой поток предупреждений имеет свой active переключатель. То есть ваш основной поток может деактивировать отдельные потоки предупреждений контролируемым образом. См. Этот пример:

 import threading import time class WarnThread(threading.Thread): def __init__(self, timeout, name): threading.Thread.__init__(self) self._timeout = timeout self.active = True self.name = name self.start() def run(self): self._warnafter() def _warnafter(self): time.sleep(self._timeout) if self.active: print("WarnThread %s: Warning after timeout" % self.name) ws = [WarnThread(2, i) for i in range(5)] # Simulate spending some time doing other things, # such as responding to external events. time.sleep(1) # Selectively deactivate some of the warn threads. ws[0].active = False ws[2].active = False for w in ws: w.join() 

Вывод:

 WarnThread 4: Warning after timeout WarnThread 1: Warning after timeout WarnThread 3: Warning after timeout 
  • Python IDLE совместим с многопоточным?
  • Как запустить фоновый процесс при постоянной проверке ввода - потоки?
  • fcntl.flock - как реализовать тайм-аут?
  • Потоки Python. Как заблокировать поток?
  • Глобальная переменная Python с потоком
  • Является ли RLock разумным дефолтом по блокировке?
  • Python ThreadPoolExecutor - проверяется ли обратный вызов в том же потоке, что и отправленный func?
  • python: Fatal IO error 11 (ресурс временно недоступен) на сервере X: 0.0
  • Python - лучший язык программирования в мире.