如何在后台运行无限循环并停止它?

1 投票
3 回答
4307 浏览
提问于 2025-04-18 15:09

我遇到的问题是:我想用Tkinter点击一个按钮,然后启动一个Python脚本。这个Python脚本现在是一个模块(我不知道这样做是否是最好的方式),它被导入到我的主脚本中。这个脚本应该在后台运行。里面有一个方法可以让它退出。我该怎么调用这个方法呢?我想给这个模块传一个标志或者其他什么东西,但我不知道该怎么做。

目前,我在gui.py中是这样调用我的代码的:

import sniffer_wideband_v09
from Tkinter import *
root = Tk()
def handle_click():
    global t
    global stop_flag
    stop_flag = 0

    def callback():
        sniffer_wideband_v09.main(sampling_rates, center_frequencies, gains, file_dir, files_names) 
    t = Thread(target=callback)
    t.start()
root.mainloop()

我想在sniffer_wideband_v09.py中调用quitting()方法,或者给我的模块传一个标志来停止无限循环。之后,我还需要把这些都绑定到Tkinter的按钮上。

我对这个问题做了一些研究,发现了:

有没有办法终止Python中的线程?,还有 如何在Tkinter的后台运行一个函数,以及 如何在Python线程中运行和停止一个无限循环

第一个链接看起来很有希望,但我还不完全理解,我正在努力研究。

注意:我直接从我的终端运行它,命令是./gui.py,我使用的是Ubuntu,而不是Windows。(我觉得这可能会影响多线程的处理方式)。

谢谢你的阅读,任何提示或帮助都将不胜感激。如果我找到解决方案,我会发布我的代码。

编辑:为了提供更多关于在线程中启动的脚本的信息:(这是一个GNURadio脚本)

class foo():
    def __init__(self):
         self.parameters()
    def methods():
         self.dostuff()
def main(sampling_rates, center_frequencies, gains, file_dir, files_names):
    tb = foo()
    while True: #flag could be here to exit the infinite while.
         tb.start()
    tb.stop()
def quitting():
    tb.stop()
 ## not mandatory piece of code from now on
if __name = "__main__":
     main()
     quitting()

3 个回答

0

我花了好几个小时试着解决你的问题,顺便也想学习一下,使用了线程,结果遇到了几个无响应的界面,最后得出了这个结论:TkInter 不是线程安全的,具体可以参考这个链接:tkinter tkMessageBox 在线程中无法工作

1

图形用户界面(GUI)工具包通常是基于事件驱动的。这意味着程序会在一个循环中运行,处理各种事件,比如鼠标移动、点击、键盘输入、定时器等。

在一个事件驱动的程序中,通常有三种方法可以让长时间运行的计算继续进行:

  • 定时器和回调
  • 单独的进程
  • 线程

第一种方法相对简单。你可以设置一个定时器。当定时器到时间了,它会产生一个事件或者调用一个回调函数(这取决于你使用的工具包)。在回调中,你可以做一点工作,保存计算的状态,可能还会更新一个进度指示器,然后重新启动定时器,最后退出回调。这种方法和GUI结合得很好,但你的代码需要结构合理,能够把工作分成小块。如果回调执行得太久,事件处理就会受到影响,GUI会变得不响应。回调的执行时间最好在10到100毫秒之间。这种方法无法利用现代CPU的多核优势。

第二种方法是启动一个单独的进程,正如mguijarr所演示的那样。在CPython中,这是一种非常好的解决方案,因为这样不会影响到GUI,而且你可以通过各种方式(信号、套接字、共享内存、消息队列)与外部程序进行通信。这种方法可以充分利用多个核心。使用multiprocessing.Pool,你甚至可以将工作分配到所有可用的核心上。

而线程在CPython中并不是一个理想的解决方案。全局解释器锁("GIL")限制了CPython解释器,使得一次只能有一个线程执行Python字节码。这最初是为了简化内存管理。因此,即使你使用一个单独的线程进行计算,也可能会让GUI缺乏处理器时间,从而变得不响应。而且,你需要用锁来保护多个线程使用的数据。对于大多数GUI工具包来说,只有一个线程可以调用工具包的函数。所以你不能从第二个线程更新进度指示器。

3

因为你的Tkinter图形界面应用和你从sniffer_wideband_09模块调用的主代码之间没有互动,所以我建议你使用多进程:

import sniffer_wideband_v09
import multiprocessing
from Tkinter import *
root = Tk()
def handle_click():
    global t

    t = multiprocessing.Process(target=sniffer_wideband_v09.main, args=(sampling_rates, center_frequencies, gains, file_dir, files_names))
    t.start()
root.mainloop()

当你想要停止这个进程时,只需要调用t.terminate()。想了解更多,可以查看多进程模块的文档。

撰写回答