Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么我要在Python中运行一个额外的线程?_Python_Python 3.x_Multithreading_Raspberry Pi_Raspberry Pi3 - Fatal编程技术网

为什么我要在Python中运行一个额外的线程?

为什么我要在Python中运行一个额外的线程?,python,python-3.x,multithreading,raspberry-pi,raspberry-pi3,Python,Python 3.x,Multithreading,Raspberry Pi,Raspberry Pi3,我在和一个树莓派合作 我有一个按钮连接到GPIO引脚,一个LED连接到另一个引脚。按下按钮时,将调用一个函数。 当该功能激活时,我希望LED闪烁,这需要一个背景线程。这本质上意味着我需要一个后台线程在我的按钮处理程序运行时运行,并在我的按钮处理程序停止时停止 下面的代码演示了我遇到的问题。代码以一个线程开始,但当我按下按钮时,threading.active_count()显示有3个线程正在运行(不是预期的2个)。当我的线程运行完毕后,剩下2个后台线程,而不是预期的1个 这是我的代码: #!/u

我在和一个树莓派合作

我有一个按钮连接到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\u event\u detect
行,并添加对我的btn\u处理程序函数的手动调用(
btn\u处理程序(1)
),我没有这个问题。函数运行完毕后,根据
active\u count()
,我将线程数减少到1个。不管问题是什么,它似乎与我在GPIO事件处理程序函数中启动线程有关

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

编辑2:还要注意,当我只运行两个线程时(我只希望有一个线程),如果我添加代码来检查线程的名称,额外的线程被称为“Dummy-3”

隐式启动以处理执行回调:

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

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

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


我觉得奇怪的是,在我真正按下按钮并触发btn_处理程序之前,只有一个线程(根据active_count())——主线程。如果RPi.GPIO运行的是一个单独的线程,它监视一个边缘并调用回调函数,那么当您使用
GPIO注册第一个回调函数时,它不应该立即运行吗,但是为了避免引起主线程的问题,当主线程接收到与回调相关的中断时,它只是将事件回调信息排队。如果不存在事件处理线程,则在发生第一个带有回调的事件时启动一个线程,以开始处理队列。