Python 从另一个线程启动/停止线程会导致意外行为

标签 python python-3.x multithreading

在研究了如何正确地要求线程停止之后,我陷入了意想不到的行为。

我正在开展一个个人项目。我的目标是在 RaspberryPi 上运行一个专用于家庭自动化的程序。

我的代码结构如下:

  1. 第一个线程专用于调度:每天的同一时间,我在 GPIO 输出上发送信号
  2. 第二个线程专门用于监视键盘的手动事件
  3. 每当按下特定键时,我想启动一个新线程,专门用于另一个例程,就像我的第一个线程一样

这是我的操作方法:

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 线程时,都会调用快门例程,但是:

  • 我的 shutter 例程中的 job 执行了两次
  • 第一个线程中的作业现在每次按计划执行两次!
  • 一旦我按下按键要求快门线程停止,加热(第一个)线程就会恢复到其原始(且正确)的行为,但是shutter 线程不停止

我不知道为什么启动这个新线程会对另一个线程的行为产生这样的修改。为什么我的停止事件不起作用?

我做错了什么?

最佳答案

由于您使用 schedule 框架来管理任务,一个干净的解决方案是使用同一框架的 API 来取消作业(而不是使用 threading.Event)。这样,任务管理就保留在计划内,而用户交互则由线程处理。

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)

关于Python 从另一个线程启动/停止线程会导致意外行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51565356/

相关文章:

python - Numpy 随机函数为给定参数创建不一致的形状

java - 调用 Thread.start() 时 onSurfaceTextureUpdated 中出现异常

python - 神经网络MPC

python - pandas 重新采样时间序列数据 - 同一列上有多个聚合函数?

python gevent : unexpected output in KeyboardInterrupt

python - <copy-of> XLST 标签在不同处理器中的应用不一致

python - point 参数对 matplotlib 中的 fiddle 图有什么作用

python - 为 Yocto 创建配方时,python3 中没有名为 'datetime' 的模块,但 python2.7 中存在日期时间

c - WinSock - UDP 广播监听器

multithreading - 暂停线程少于一毫秒