如何在Python GUI中集成可终止进程/线程?

如何在Python GUI中集成可终止进程/线程?,python,multithreading,kill-process,Python,Multithreading,Kill Process,总而言之,我对python真的是个新手,我面临着一个我不能完全掌握的任务 我已经用Tkinter创建了一个界面,它应该可以完成两个显然很简单的壮举。 通过单击“开始”按钮,将启动两个线程/进程(每个线程/进程调用多个子函数),主要从串行端口(当然,每个进程一个端口)读取数据并将其写入文件。 I/O操作在while循环中循环,该循环具有非常高的计数器,以允许它们几乎无限期地继续进行 “停止”按钮应停止采集,基本上应: 终止读/写线程 关闭文件 关闭串行端口 不幸的是,我仍然不理解如何完成第1点,即

总而言之,我对python真的是个新手,我面临着一个我不能完全掌握的任务

我已经用Tkinter创建了一个界面,它应该可以完成两个显然很简单的壮举。 通过单击“开始”按钮,将启动两个线程/进程(每个线程/进程调用多个子函数),主要从串行端口(当然,每个进程一个端口)读取数据并将其写入文件。 I/O操作在while循环中循环,该循环具有非常高的计数器,以允许它们几乎无限期地继续进行

“停止”按钮应停止采集,基本上应:

  • 终止读/写线程
  • 关闭文件
  • 关闭串行端口
  • 不幸的是,我仍然不理解如何完成第1点,即:如何创建可终止线程而不终止整个GUI。有没有办法做到这一点


    谢谢大家!

    首先可以使用
    subprocess.Popen()
    生成子进程,然后可以使用
    Popen.terminate()
    终止它们


    请注意,如果愿意,您也可以在单个Python线程中完成所有操作,而不需要子进程。完全可以在单个事件循环中“多路复用”来自多个端口的读取。

    首先,您必须选择是使用线程还是进程

    我不会过多地讨论差异,用谷歌搜索它;)无论如何,这里有一些事情需要考虑:在线程之间建立通信要比在进程之间建立通信容易得多;在Python中,所有线程都将在同一个CPU内核上运行(请参阅),但子进程可能会使用多个内核

    过程

    如果您使用的是子流程,有两种方法:
    subprocess.Popen
    multiprocessing.Process
    。使用
    Popen
    可以运行任何东西,而
    Process
    提供了一个更简单的线程式接口来运行python代码,它是子流程中项目的一部分

    可以使用
    terminate
    方法杀死这两个对象

    有关和的详细信息,请参阅文档

    当然,如果您想要更优雅的退出,您将希望向子流程发送一条“退出”消息,而不仅仅是终止它,以便它有机会进行清理。你可以这样做,比如写信给它的stdin。进程应该从stdin读取,当它收到消息“exit”时,它应该在退出之前执行您需要的任何操作

    线程

    对于线程,您必须实现自己的停止机制,而不是使用像
    process.terminate()
    这样的暴力手段

    通常,一个线程在一个循环中运行,在该循环中,您检查是否有一个表示停止的标志。然后你从循环中挣脱出来

    我通常有这样的东西:

    class MyThread(Thread):
        def __init__(self):
            super(Thread, self).__init__()
            self._stop_event = threading.Event()
    
        def run(self):
            while not self._stop_event.is_set():
                # do something
                self._stop_event.wait(SLEEP_TIME)
            # clean-up before exit
    
        def stop(self, timeout):
            self._stop_event.set()
            self.join(timeout)
    
    当然,您需要一些异常处理等,但这是基本思想

    编辑:评论中问题的答案

    线程。启动新线程(您的函数)
    启动一个新线程,这是正确的。另一方面,模块
    threading
    为您提供了更高级别的API,其性能要好得多

    使用
    threading
    模块,您可以对以下各项执行相同的操作:

    t = threading.Thread(target=your_function)
    t.start()
    
    或者,您可以创建自己的类,该类继承自
    Thread
    ,并将功能放在
    run
    方法中,如上面的示例所示。然后,当用户单击开始按钮时,您将执行以下操作:

    t = MyThread()
    t.start()
    
    您应该将t变量存储在某个地方。具体位置取决于应用程序其余部分的设计方式。我可能会有一个对象,其中包含列表中所有活动线程

    当用户单击“停止”时,您应该:

    t.stop(some_reasonable_time_in_which_the_thread_should_stop)
    

    之后,您可以从列表中删除
    t
    ,它不再可用。

    谢谢您的详细回答!我想我错过了线程的要点。在我的例子中,带有while循环的函数是由
    线程在线程中生成的。启动新线程(带有while的函数)
    ,但是我应该循环线程而不是函数本身。对吗?在这种情况下,如何向线程传递停止标志?线程将在按下“开始”按钮时生成,但停止由另一个Tkinter对象(“停止”按钮)处理。使线程成为全局线程就足够了吗?谢谢,但关键是当用户单击停止时,线程应该意识到停止标志已被提升并停止,否则继续在串行端口上获取。没有“合理的停止时间”,因为除非另有说明,否则它应该继续获取。这是我发现非常困难的部分,它不允许我在采集步骤中插入“睡眠”点,否则我将从端口丢失包。
    stop
    的timout参数是可选的。您可以将其设置为
    None
    。有关详细信息,请参阅文档。如果您很好地实现了run方法,这将立即停止。设置超时的原因是为了处理线程的问题。例如,如果
    run
    中出现错误,导致其从不停止,则调用
    stop
    (调用
    join
    )将冻结主线程。当线程没有响应时,超时告诉函数何时放弃。在这种情况下,您要做什么又取决于您。谢谢,这很清楚,事实上这正是发生的事情:我将我的线程实现为一个线程类(即:
    MyThread(threading.thread)
    ),并定义了一个join属性(它在Python中的名称是否与之类似?):timeout是none,当我单击“Stop”时,整个GUI冻结按钮并停止线程…
    thread.join()
    没有什么作用。它只是表示“等待此线程完成”。它不会导致线程停止。你必须告诉它停止,然后等待它停止。顺便说一句,这是一个很好的解释为什么它需要
    timeout
    参数-意思是“等待这个线程完成,但不要等待超过X秒”