简体   繁体   中英

How to run an infinite loop in background and stop it?

Here is my problem: Using Tkinter, I want to click a button and launch a python script. This python script is now a module (I don't know if it s the best way to do it) imported to my main script. This script should be running in background. There is a method in it to quit it. How can I call it ? I wanted to pass a flag or something to the module but I don't know how to do that.

For now, I call my code like that: in 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()

I would like to call the quitting() method in sniffer_wideband_v09.py. Or to pass a flag to my module to stop the infinite loop. Afterward, I will need to bind all of this to Tkinter Buttons.

I did a bit of research on the question and found :

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

The first is promising, but I don't fully understand it, I'm working on it.

Note: I run it directly from my shell with ./gui.py and I am under Ubuntu, not windows.(I think it can change some ways of dealing with multi threading).

Thanks for the reading, any hint or help will be appreciated. I will post my code if I find a response in the mean time.

Edit: To give more info about the script launched in the thread : (it is a GNURadio script)

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()

Since there is no interaction between your Tkinter gui application and the main code you're calling from the sniffer_wideband_09 module I would recommend you to use multiprocessing:

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()

When you want to stop this process just call t.terminate() . Read more on the multiprocessing module documentation.

GUI toolkits are generally event-driven. This means that the program runs in a loop processing events (mouse movements, clicks, keystrokes, timers et cetera).

There are generally three ways to have a long-running computation running in an event-driven program;

  • timer and callback
  • separate process
  • threads

The first solution is relatively simple. You set up a timer. This timer will produce an event or call a callback function (depends on the toolkit) after it runs out. You do a little bit of work in the callback, save the state of the computation, possibly update a progress indicator, restart the timer and exit the callback. This blends in well with the GUI but requires your code to be structured so that the work can be divided into small chunks. If the callbacks take too long, event processing will suffer and the GUI will feel unresponsive. The callback should run no longer than eg in between 10 and 100 ms. This method cannot take advantage of the multiple cores prevalent in modern CPUs.

The second solution is to start a separate process, as mguijarr demonstrated. On CPython this is a very good solution because you're this will not influence the GUI at all, and you can still communicate with the external program using all kinds of methods (signals, sockets, shared memory, message queues). This will take advantages of multiple cores. Using a multiprocessing.Pool you can even distribute work over all available cores.

Threads on the other hand are not an optimal solution in CPython in this case. The Global Interpreter Lock ("GIL") restricts the CPython interpreter so that only one thread at a time can execute Python bytecode. This was originally done to simplify memory management. So even if you are using a separate thread for computations, you can still starve the GUI of processor time and make in unresponsive. And you have to protect data used by multiple threads with locks. And with most GUI toolkits, only one thread can call toolkit functions. So you cannot eg update a progress indicator from a second thread.

我花了几个小时尝试使用线程来解决您自己的问题,后来几个无响应的UI最终导致:TkInter不是线程安全的, 因为tkinter tkMessageBox无法在thread中工作

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM