【问题标题】:Why am I getting an extra thread running in Python?为什么我在 Python 中运行了一个额外的线程?
【发布时间】:2019-03-12 21:26:59
【问题描述】:

我正在使用 Raspberry Pi。

我有一个连接到 GPIO 引脚的按钮,以及一个连接到不同引脚的 LED。当按钮被按下时,一个函数被调用。 当该功能处于活动状态时,我希望 LED 闪烁,这需要一个后台线程。这实质上意味着我需要一个后台线程来在我的按钮处理程序运行时运行,并在我的按钮处理程序停止时停止。

我遇到的问题是通过运行下面的代码来证明的。代码以单个线程开始,但是当我点击按钮时,threading.active_count() 显示有 3 个线程正在运行(而不是预期的 2 个)。当我的线程完成运行后,我剩下 2 个后台线程 - 而不是预期的 1 个。

这是我的代码:

#!/usr/bin/env python3

import RPi.GPIO as GPIO
import time
import threading
from threading import Thread, Event

#########################
# Function to Blink LED #
#########################

# Sample function that blinks the LED
def blink_led_func(led, stop_blinking):
    while not stop_blinking.is_set():
        print("Blinking LED...")
        time.sleep(0.5)

#############
# Decorator #
#############

# Starts a background thread which blinks the LED, runs the decorated
# function, and when the function is done running, stops blinking the LED
class blink_led:
    def __init__(self, function):
        self.f = function

    def __call__(self, channel):
        stop = Event()
        t = Thread(target=blink_led_func, args=(1, stop))
        t.start()

        self.f(channel)

        stop.set()
        t.join()

##################
# Button Handler #
##################

# Called when button is pressed
@blink_led
def btn_handler(channel):
    print("Button pressed")
    time.sleep(5)

##############
# Setup GPIO #
##############

# Setup pin
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_UP)

##############################
# Add Button Event Listeners #
##############################

GPIO.add_event_detect(12, GPIO.FALLING, callback=btn_handler, bouncetime=300)

########
# Main #
########

print("Listening for button presses...")

i = 0
while True:
    time.sleep(1)
    print("%s threads running" % threading.active_count())

这是我的代码的输出:

Listening for button presses...
1 threads running
1 threads running
1 threads running
Blinking LED...
Button pressed
3 threads running
Blinking LED...
Blinking LED...
3 threads running
Blinking LED...
Blinking LED...
3 threads running
Blinking LED...
Blinking LED...
3 threads running
Blinking LED...
Blinking LED...
3 threads running
Blinking LED...
Blinking LED...
Button pressed
3 threads running
Blinking LED...
Blinking LED...
3 threads running
Blinking LED...
Blinking LED...
3 threads running
Blinking LED...
Blinking LED...
3 threads running
Blinking LED...
Blinking LED...
3 threads running
Blinking LED...
2 threads running
2 threads running
2 threads running

这让我大吃一惊,因为在我的真实代码中,我有一个 Ctrl+C 处理程序,它说:使用 threading.Event() 通知所有线程死亡,等到active_count() == 1(只剩下主线程),清理 GPIO 并退出。理论上,这应该可以防止后台线程在清理后尝试使用 GPIO 库闪烁(这会导致异常),但在实践中,它会卡住等待其他线程死亡,因为总是有 2出于某种原因。

我做错了吗?还是 GPIO 库做了一些时髦的事情?

编辑:如果我注释掉 GPIO.add_event_detect 行并改为手动调用我的 btn_handler 函数 (btn_handler(1)),我就没有这个问题。函数运行完成后,我根据active_count() 减少到 1 个线程。不管是什么问题,似乎都与我在 GPIO 事件处理函数中启动线程有关。

还要注意,如果我不在 btn_handler 中启动后台线程,active_count() 在整个运行过程中保持为 1,据我所知,GPIO 库没有运行任何后台线程。

编辑 2:还请注意,当我减少到 2 个正在运行的线程时(当我只希望有一个时),如果我添加代码来检查线程的名称,额外的线程称为“Dummy-3 "

【问题讨论】:

    标签: python python-3.x multithreading raspberry-pi raspberry-pi3


    【解决方案1】:

    RPi.GPIO's event handling is performed in a dedicated thread 隐式启动以处理正在执行的回调:

    RPi.GPIO 为回调函数运行第二个线程。这意味着回调函数可以与主程序同时运行,以立即响应边缘。

    无论注册多少回调,这些线程都只有一个:

    [T]回调函数按顺序运行,而不是同时运行。这是因为只有一个线程用于回调,其中每个回调都按照定义的顺序运行。

    【讨论】:

    • 在我真正按下按钮并触发我的 btn_handler 之前,我似乎很奇怪,只有一个线程(根据 active_count())——主线程。如果 RPi.GPIO 正在运行一个单独的线程来监视边缘并调用回调函数,那么它不应该在您使用 GPIO.add_event_detect 注册第一个回调函数后立即运行吗?
    • @John:我不知道内部情况,但猜测,实际事件是基于中断的,但为了避免导致主线程出现问题,当主线程接收到与回调,它只是将事件回调信息排队。如果不存在事件处理线程,则在第一个带有回调的事件发生时启动一个开始处理队列。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-16
    • 2019-08-19
    • 2016-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多