【问题标题】:How to run an infinite loop in background and stop it?如何在后台运行无限循环并停止它?
【发布时间】:2014-07-29 08:05:57
【问题描述】:

这是我的问题:使用 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 Buttons。

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

Is there any way to kill a Thread in Python? 随着 How to run a function in the background of tkinterHow to run and stop an infinite loop in a python thread

第一个很有希望,但我不完全理解,我正在努力。

注意:我使用 ./gui.py 直接从我的 shell 中运行它,并且我在 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()

【问题讨论】:

    标签: python multithreading python-2.7 tkinter


    【解决方案1】:

    由于您的 Tkinter gui 应用程序和主应用程序之间没有交互 你从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()。阅读有关多处理模块文档的更多信息。

    【讨论】:

    • 谢谢,我会试试这个。目前,没有互动。之后,我希望通过 2 个 Tkinter 按钮启动和停止 sniffer_wideband_v09。
    • 事实上,如果有交互,你甚至可以使用多进程,有一种队列机制可以将信息从子进程和父进程传递给/传递给。
    • 确实,我的按钮可以正常工作。我将研究python中线程和进程之间的区别。我知道这些概念,但这是我第一次在 python 代码中使用它们。我还需要看看到底发生了什么,如果“终止”没有弄乱脚本的结尾(因为它使用了一块特殊的硬件和写入文件)
    • 来自文档:终止发送 SIGTERM 信号。如果需要,您可以设置一个信号处理程序来进行一些清理。
    • @mguijarr 在两个进程之间进行通信时,使用multiprocessing.Pipe 就足够了。 Queue 的开销更大,因为它允许两个以上的进程使用它。
    【解决方案2】:

    GUI 工具包通常是事件驱动的。这意味着程序在循环中运行,处理事件(鼠标移动、点击、击键、计时器等)。

    通常有三种方法可以在事件驱动程序中运行长时间运行的计算;

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

    第一个解决方案比较简单。你设置了一个计时器。这个计时器在它用完后会产生一个事件或调用一个回调函数(取决于工具包)。您在回调中做一些工作,保存计算状态,可能更新进度指示器,重新启动计时器并退出回调。这与 GUI 很好地融合在一起,但需要对代码进行结构化,以便可以将工作分成小块。如果回调时间过长,事件处理会受到影响,并且 GUI 会感觉没有响应。回调的运行时间不应超过例如在 10 到 100 毫秒之间。这种方法无法利用现代 CPU 中普遍存在的多核。

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

    另一方面,在这种情况下,线程并不是 CPython 中的最佳解决方案。全局解释器锁(“GIL”)限制了 CPython 解释器,因此一次只有一个线程可以执行 Python 字节码。最初这样做是为了简化内存管理。因此,即使您使用单独的线程进行计算,您仍然可以使 GUI 处理器时间匮乏并且无响应。而且您必须使用锁保护多个线程使用的数据。并且对于大多数 GUI 工具包,只有一个线程可以调用工具包函数。所以你不能例如从第二个线程更新进度指示器。

    【讨论】:

    • 感谢 Roland 的回答。这确实是我所理解的:stupidpythonideas.blogspot.it/2013/10/… 回调可能是一团糟,线程在 python 中不太适合,进程可能很棒。我必须调查一下如何处理我的第二个过程,我将关闭这个问题。 (以防万一有人带着很好的解释/另一个想法进来。
    • @Paul 如果您使用multiprocessing.Process 在单独的进程中启动Python 函数,您可以使用Pipe 来交换消息。在您的 GUI 中,您通常可以使用 idle 回调来检查管道中是否有数据。
    【解决方案3】:

    我花了几个小时尝试使用线程来解决您的问题,并使用线程,但后来几个无响应的 UI 以这种方式结束:TkInter 不是线程安全的,来自 tkinter tkMessageBox not working in thread.

    【讨论】:

    • 很少(如果有的话)GUI 工具包是完全线程安全的,因为这将需要不可接受的开销,因为几乎每条共享数据都需要由互斥锁保护。有几个限制只能从一个线程调用工具包函数。
    猜你喜欢
    • 2012-04-30
    • 2018-06-18
    • 1970-01-01
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    相关资源
    最近更新 更多