在 Python 中防止一个线程的多个实例

Preventing multiple instances of a thread in Python

我正在使用 Raspberry Pi 到 运行 电机关闭按钮和一些 LED。 LED 必须根据某些变量执行闪烁模式,我将它们 运行 设置在它们自己的线程中以允许代码继续工作(当然,我的想法来自 here)。

问题是,根据用户按下按钮的速度(一个序列需要多次按下来设置速度),释放按钮时停止的 LED 线程有时会在最后一次之前再次调用一个已经终止了。

这意味着,我认为,现在有两个线程,所以当发送停止 LED 线程中 run() 动作的命令时,我相信其中一个线程(或两个线程)不会' 获取停止命令,因此在按钮永久释放后 LED 保持闪烁(如线程所要求的)。

所以我认为我正在寻找的是一种确保一次只有一个 LED 线程实例 运行s 的方法。我认为最简单的方法是让 LED 线程作为第一步,终止可能 运行ning 的所有其他 LED 线程实例。我喜欢这样,因为无论哪个灯从 Thread 闪烁,它都会停止,并且来自最近一个要求闪烁灯的操作的新线程接管。

我该怎么做?我一直在研究 isActive() 函数,但我猜我无法将其概念化,所以我不知道如何使用它。

这里是LED线程供参考。它由其他 .py 脚本使用 flash_led() 函数调用,并使用 stop_led() 函数停止:

## leds.py. This is imported into the scripts that run the motors and buttons

# LED control Thread (Which-LED, Desired-State On/Off/Flash, Flash-On-Duration, Flash-Off-Duration, Number-Of-Flashes)

class LEDFlash(Thread):

    #Initial parameters for an LED Flash command, overwritten by led_flash()
    led_pin = led_dict["red_led_1"] #from a dict earlier in the script
    flash_on_duration = 1.0
    flash_off_duration = 1.0

    def __init__(self):
        super(LEDFlash, self).__init__()
        self.keep_flashing = True

    def run(self):
        while self.keep_flashing:
            GPIO.output(self.led_pin, GPIO.HIGH)
            time.sleep(self.flash_on_duration)
            GPIO.output(self.led_pin, GPIO.LOW)
            time.sleep(self.flash_off_duration)

    def stop_flash(self):
        self.keep_flashing = False


def flash_led(led, on_time, off_time): # 'flash_led.' added to make flash_thread accessible from the stop_led() function
    flash_led.flash_thread = LEDFlash()
    flash_led.flash_thread.led_pin = led_dict[str(led)]
    flash_led.flash_thread.flash_on_duration = on_time
    flash_led.flash_thread.flash_off_duration = off_time
    flash_led.flash_thread.start()

def stop_led():
    flash_led.flash_thread.stop_flash()

如果我没理解错的话,你问的是如何停止 python 中的线程。我喜欢该主题的现有 post:Is there any way to kill a Thread in Python? .

话虽如此,您可能还需要考虑 在释放按钮时停止线程的选项,因为创建线程是一项相对昂贵的操作,并且,作为在上面的线程中提到,您可能不应该完全终止该线程——您将希望等待它响应一个事件。如果你沿着这条路走下去,你也可能有一个状态改变的事件,并且只是通知现有线程它应该处于什么状态,而不是创建一个最终会处于相同状态的全新线程.

@MichaelNelson 的建议是只有一个处理 LED 的永久线程确实是最好的方法。

但是要直接解决你的问题,你可以在flash_led()的开头添加这段代码:

if hasattr(flash_led, 'flash_thread'):
  stop_led()

这将确保在创建新线程之前销毁旧线程(如果有)。

编辑:旧线程和新线程仍然有可能在短时间内都处于活动状态;这对你来说应该不是问题,但如果是,你可以说:

if hasattr(flash_led, 'flash_thread'):
  stop_led()
  flash_led.flash_thread.join() # wait until old thread completes

你绝对不想杀死一个线程;正如 here 所解释的那样,终止一个线程是极其危险和混乱的,只有在其他方法都行不通的极少数情况下才应该这样做。你的情况是完全标准的,可以用非常干净的代码来处理,不需要诉诸任何丑陋的东西。

此外,我建议将函数属性替换为全局变量;它们大致相同,但是全局变量更容易跟踪有人何时阅读您的代码。也就是说,只要写:

flash_thread = None
def flash_led(led, on_time, off_time):
    # you need global statement at the beginning of every function
    # in which you modify a global variable
    global flash_thread
    if flash_thread is not None:
      flash_thread.stop_flash() # probably no need to define stop_led() 
    flash_thread = LEDFlash()
    flash_thread.led_pin = ...
    ...

更好的方法是重构代码以将其存储为某些控制器对象的实例属性,但这需要更多工作。