【问题标题】:How to terminate a thread in Python?如何终止 Python 中的线程?
【发布时间】:2020-05-06 08:00:19
【问题描述】:

我知道这个话题已经讨论过几次了,但是我已经尝试过,或者至少尝试了几乎所有的解决方案,但是作为一个相当新的 python basher,我无法让以前的任何解决方案发挥作用。

脚本的基本前提是它订阅了一个 MQTT 代理并等待命令,单个动作命令 100% 工作,但是其中一个命令需要一个循环无限期地运行,直到收到另一个命令,因此最适当的解决方案是在单独的线程中运行“循环”,而主订阅者循环继续“侦听”下一个命令。

一切都在工作 95%,“静态”命令通过并​​且任务运行良好,然后当“mtg”命令通过它时,线程和循环运行 100%,但是这是它失败的地方,当接收到下一个命令时,我可以确认“if”语句在将消息打印到控制台时处理命令,但是 thread.stop() 没有运行,或者它可能运行但它不会终止线程--- 我正在拔头发想弄明白。

一些代码:

from sys import exit
import blinkt
import threading
import time

MQTT_SERVER = '192.168.x.x'
MQTT_PORT = 1883
MQTT_TOPIC = 'mytopic'

REDS = [0, 0, 0, 0, 0, 16, 64, 255, 64, 16, 0, 0, 0, 0, 0, 0]

start_time = time.time()

class task(threading.Thread):

     def __init__(self):
         threading.Thread.__init__(self)
         self.kill = threading.Event()
         self.event = threading.Event()
         self._stop = threading.Event()

     def run(self):
#        while not self.kill.is_set(): 
         while True:
             if self.stopped():
                return
             self.start_run()

     def stop(self):
#        self.event.set()
         self._stop.set()

     def stopped(self):
         return self._stop.isSet()

     def start_run(self):
#         while True: <-- no longer needed as the run method loops the process. 
             delta = (time.time() - start_time) * 16
             offset = int(abs((delta % len(REDS)) - blinkt.NUM_PIXELS))

             for i in range(blinkt.NUM_PIXELS):
                 blinkt.set_pixel(i, REDS[offset + i], 0, 0)

             blinkt.show()
             time.sleep(0.1)

def on_connect(client, userdata, flags, rc):
    print('Connected with result code ' + str(rc))
    client.subscribe(MQTT_TOPIC)

def on_message(client, userdata, msg):

    data = msg.payload
    if type(data) is bytes:
        data = data.decode('utf-8')
    data = data.split(',')
    command = data.pop(0)

    if command == 'clr' and len(data) == 0:
        blinkt.clear()
        blinkt.show()
        t1.stop()      #<--- I've tried a few ways to get the task to stop when the "clr" command is recieved
        task.stop()
        return

    if command == 'rgb' and len(data) == 4: #<-- This code block works fine, msg arrives and LEDs are set correctly
        try:
            pixel = data.pop(0)

            if pixel == '*':
                pixel = None
            else:
                pixel = int(pixel)
                if pixel > 7:
                    print('Pixel out of range: ' + str(pixel))
                    return

            r, g, b = [int(x) & 0xff for x in data]

            print(command, pixel, r, g, b)

        except ValueError:
            print('Malformed command: ' + str(msg.payload))
            return
        if pixel is None:
            for x in range(blinkt.NUM_PIXELS):
                blinkt.set_pixel(x, r, g, b)
        else:
            blinkt.set_pixel(pixel, r, g, b)
        blinkt.show()
        return


    if command == 'mtg' and len(data) == 0:
        print(command)
        t1 = task()
        t1.start()   #<-- Here is where the Thread is called to start and seems to run ok
        return

blinkt.set_clear_on_exit()

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(MQTT_SERVER, MQTT_PORT, 60)
client.loop_forever()

【问题讨论】:

    标签: python multithreading mqtt paho


    【解决方案1】:

    您的 t1 变量是本地变量,因此当您退出 on_message 函数时它会丢失。此外,您将task 类和t1 实例混为一谈(task.stop() 不起作用)。

    为了快速修复,将t1 = None 声明为全局,然后将global t1 添加到您的on_message 函数中...

    但是,我会考虑重构一些东西,以便有一个始终运行的线程来命令 Blinkt!,并且 MQTT 消息处理程序只需相应地设置其状态 - 就像这样。很明显,干编码,所以可能有些愚蠢。

    from sys import exit
    import blinkt
    import threading
    import time
    
    MQTT_SERVER = "192.168.x.x"
    MQTT_PORT = 1883
    MQTT_TOPIC = "mytopic"
    
    REDS = [0, 0, 0, 0, 0, 16, 64, 255, 64, 16, 0, 0, 0, 0, 0, 0]
    
    start_time = time.time()
    
    
    class BlinktManager(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.stop_event = threading.Event()
            self.mode = None
    
        def run(self):
            while not self.stop_event.isSet():
                self.tick()
                self.stop_event.wait(0.1)  # instead of sleep
    
        def tick(self):
            if self.mode == "reds":
                self._tick_reds()
    
        def _tick_reds(self):
            delta = (time.time() - start_time) * 16
            offset = int(
                abs((delta % len(REDS)) - blinkt.NUM_PIXELS)
            )
    
            for i in range(blinkt.NUM_PIXELS):
                blinkt.set_pixel(i, REDS[offset + i], 0, 0)
    
            blinkt.show()
    
        def clear(self):
            self.mode = None
            blinkt.clear()
            blinkt.show()
    
        def set_all_pixels(self, r, g, b):
            self.mode = None
            for x in range(blinkt.NUM_PIXELS):
                blinkt.set_pixel(x, r, g, b)
            blinkt.show()
    
        def set_pixel(self, x, r, g, b):
            self.mode = None
            blinkt.set_pixel(x, r, g, b)
            blinkt.show()
    
        def begin_reds(self):
            self.mode = "reds"
    
    
    def on_connect(client, userdata, flags, rc):
        print("Connected with result code " + str(rc))
        client.subscribe(MQTT_TOPIC)
    
    
    def on_message(client, userdata, msg):
        data = msg.payload
        if type(data) is bytes:
            data = data.decode("utf-8")
        data = data.split(",")
        command = data.pop(0)
    
        if command == "clr" and len(data) == 0:
            blinkt_manager.clear()
    
        if command == "rgb" and len(data) == 4:
            x = data[0]
            r, g, b = [int(x) & 0xFF for x in data[1:]]
            if x == "*":
                blinkt_manager.set_all_pixels(r, g, b)
            else:
                # TODO: error handling
                blinkt_manager.set_pixel(int(x), r, g, b)
    
        if command == "mtg" and len(data) == 0:
            blinkt_manager.begin_reds()
    
    
    blinkt.set_clear_on_exit()
    
    blinkt_manager = BlinktManager()
    blinkt_manager.start()
    
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    client.connect(MQTT_SERVER, MQTT_PORT, 60)
    client.loop_forever()
    

    【讨论】:

    • 考虑到你称之为“干编码”是惊人的,我对 task.stop() 的尝试从一开始就有缺陷,我在没有充分理解前面的例子的情况下尝试了各种方法。我已经看到了你这样做的新亮点,当然除了我这边缺少导入之外,它还可以工作,除非我要添加一个小问题来点亮条带上的其余灯。跨度>
    • “新光”,呵呵。 ;-) 无论如何,很高兴我能帮上忙!
    【解决方案2】:

    Python 程序提升 python中的异常 线程

    import threading 
    import ctypes 
    import time 
    
    class thread_with_exception(threading.Thread):
    
        def __init__(self, name): 
        threading.Thread.__init__(self) 
        self.name = name 
    
    def run(self): 
    
        # target function of the thread class 
        try: 
            while True: 
                print('running ' + self.name) 
        finally: 
            print('ended') 
    
    def get_id(self): 
    
        # returns id of the respective thread 
        if hasattr(self, '_thread_id'): 
            return self._thread_id 
        for id, thread in threading._active.items(): 
            if thread is self: 
                return id
    
    def raise_exception(self): 
        thread_id = self.get_id() 
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 
              ctypes.py_object(SystemExit)) 
        if res > 1: 
            ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0) 
            print('Exception raise failure') 
    
    t1 = thread_with_exception('Thread 1') 
    t1.start() 
    time.sleep(2) 
    t1.raise_exception() 
    t1.join() 
    

    【讨论】:

      猜你喜欢
      • 2017-11-17
      • 1970-01-01
      • 2022-11-21
      • 2018-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-23
      • 1970-01-01
      相关资源
      最近更新 更多