После некоторого исследования того, как правильно просить поток остановить, я завис в неожиданном поведении.
Я работаю над личным проектом. Моя цель - запустить программу на RaspberryPi, посвященную домашним вещам.
Мой код структурирован следующим образом:
Вот как я продолжаю:
import schedule
from pynput import keyboard
import threading
first_thread = threading.Thread(target=heating, name="heating")
second_thread = threading.Thread(target=keyboard, name="keyboard")
first_thread.start()
second_thread.start()
stop_event = threading.Event()
Моя процедура нагрева определяется:
def heating():
def job():
GPIO.output(4,GPIO.HIGH)
return
schedule.every().day.at("01:00").do(job)
while True:
schedule.run_pending()
time.sleep(0.5)
Мой монитор клавиатуры определяется следующим образом:
def keyboard():
def on_press(key):
if key == keyboard.Key.f4:
shutter_thread = threading.Thread(name="shutter", target=shutter, args=(stop_event,))
shutter_thread.start()
if key == keyboard.Key.f5:
stop_event.set()
with keyboard.Listener(on_press=on_press,on_release=on_release) as listener:
listener.join()
Моя цель shutter
аналогична нагревательной:
def shutter(stop_event):
def open():
GPIO.output(6,GPIO.HIGH)
return
t = threading.currentThread()
schedule.every().day.at("22:00").do(open)
while not stop_event.is_set():
schedule.run_pending()
time.sleep(0.5)
Проблема в том, каждый раз, когда я нажимаю на кнопку, чтобы начать свою shutter
нитку, процедура спуска называется, но:
job
в моей процедуре shutter
выполняется дваждыjob
в первом потоке также выполняется дважды каждый раз, когда она выполняется по расписанию!shutter
остановиться, то heating
(первая) нить вернется к исходному (и правильному) поведению, но нить shutter
не останавливаетсяЯ понятия не имею, почему запуск этого нового потока дает такую модификацию в поведении другого потока. И почему мое событие остановки не работает?
Что я делаю неправильно?
Поскольку вы используете структуру schedule
для управления задачами, чистым решением будет использование одного и того же API рамки для отмены заданий (вместо использования threading.Event
). Таким образом, управление задачами остается в рамках schedule
а взаимодействие с пользователем обрабатывается threading
.
def keyboard():
tasks = []
def on_press(key):
if key == keyboard.Key.f4:
# Shutter task.
tasks.append(
schedule.every().day.at("22:00").do(lambda: GPIO.output(6,GPIO.HIGH))
)
if key == keyboard.Key.f5:
schedule.cancel_job(tasks.pop(-1))
with keyboard.Listener(on_press=on_press,on_release=on_release) as listener:
listener.join()
# Heating task.
schedule.every().day.at("01:00").do(lambda: GPIO.output(4,GPIO.HIGH))
# Start keyboard listener.
ui = threading.Thread(target=keyboard)
ui.start()
while True:
schedule.run_pending()
time.sleep(0.5)
Даже если решение a_guest является чистым, я могу поделиться вторым решением для тех, кто может столкнуться с подобной ситуацией.
Рабочее решение состоит в том, чтобы определить конкретный планировщик в разных потоках вместо использования по умолчанию.
Иллюстрация:
def heating():
def job():
GPIO.output(4,GPIO.HIGH)
return
heat_sched = schedule.Scheduler()
heat_sched.every().day.at("01:00").do(job)
while True:
heat_sched.run_pending()
time.sleep(1)
def shutter(stop_event):
def open():
GPIO.output(6,GPIO.HIGH)
return
shutter_sched = schedule.Scheduler()
shutter_sched.every().day.at("22:00").do(open)
while True:
if not stop_event.is_set():
shutter_sched.run_pending()
time.sleep(0.5)
else:
shutter_sched.clear()
return
schedule
иkeyboard
? Еслиschedule
отсюда , вы вызываетеrun_pending()
дважды из разных потоков, что выглядит а) неверно и б) racy.schedule
библиотек отсюда иpynput
отсюда, который предоставляет объект клавиатуры. Я не понимал,run_pending
дважды вызыватьrun_pending
будет неправильно, спасибо! Это решает первую проблему. Но у меня все еще есть проблема, связанная с остановкой моей темы.