Home > Net >  control a daemon thread with routes in flask
control a daemon thread with routes in flask

Time:02-11

How do you make two routes control a daemon thread in python

flask backend file

from flask import Flask 
from time import time,sleep
from threading import Thread


app = Flask(__name__)

def intro():
    while True:
        sleep(3)
        print (f" Current time : {time()}")


@app.route('/startbot')
def start_bot():
    global bot_thread 
    bot_thread = Thread(target=intro, daemon=True)
    bot_thread.start()
    return "bot started " 

@app.route('/stopbot')
def stop_bot():
    bot_thread.join()
    return 

if __name__ == "__main__":
    app.run()

When trying to kill the thread the curl request in the terminal does not return back to the console and the thread keeps on printing data to the terminal

the idea I had was that I would declare the variable that holds the reference to the bot_thread and use the routes to control it
to test this I used curl http://localhost:port/startbot and curl http://localhost:port/stopbot

I can start the bot just fine but when I try to kill it, I get the following

NameError: name 'bot_thread' is not defined

Any help and does and don'ts will be very appreciated

take into consideration that after killing the thread a user can create a new one and also be able to kill it

CodePudding user response:

Here is a Minimal Reproducible Example :

from threading import Thread


def intro():
    print("hello")


global bot_thread


def start_bot():
    bot_thread = Thread(target=intro, daemon=True)
    return

def stop_bot():
    if bot_thread:
        bot_thread.join()


if __name__ == "__main__":
    import time
    start_bot()  # simulating a request on it
    time.sleep(1)  # some time passes ...
    stop_bot()  # simulating a request on it
Traceback (most recent call last):
  File "/home/stack_overflow/so71056246.py", line 25, in <module>
    stop_bot()  # simulating a request on it
  File "/home/stack_overflow/so71056246.py", line 17, in stop_bot
    if bot_thread:
NameError: name 'bot_thread' is not defined

My IDE makes the error visually clear for me : the bot_thread is not used, because it is a local variable, not the global one, although they have the same name. This is a pitfall for Python programmers, see this question or this one for example.

So :

def start_bot():
    global bot_thread
    bot_thread = Thread(target=intro, daemon=True)
    return

but

Traceback (most recent call last):
  File "/home/stack_overflow/so71056246.py", line 26, in <module>
    stop_bot()  # simulating a request on it
  File "/home/stack_overflow/so71056246.py", line 19, in stop_bot
    bot_thread.join()
  File "/usr/lib/python3.9/threading.py", line 1048, in join
    raise RuntimeError("cannot join thread before it is started")
RuntimeError: cannot join thread before it is started

Hence :

def start_bot():
    global bot_thread
    bot_thread = Thread(target=intro, daemon=True)
    bot_thread.start()
    return

which finally gives :

hello

EDIT

When trying to kill the thread the curl request in the terminal does not return back to the console and the thread keeps on printing data to the terminal

@prometheus the bot_thread runs the intro function. Because it contains an infinite loop (while True) it will never reach the end of the function (implicit return) so the thread is never considered as finished. Because of that, when the main thread tries to join (wait until the thread finishes, then get the result), it waits endlessly because the bot thread is stuck in the loop.
So you have to make it possible to exit the while loop. For example (like in the example I linked in a comment), by using another global variable, a flag, that gets set in the main thread (route stop_bot) and that is checked in the intro loop. Like so :

from time import time, sleep
from threading import Thread


def intro():
    global the_bot_should_continue_running
    while the_bot_should_continue_running:
        print(time())
        sleep(1)


global bot_thread
global the_bot_should_continue_running


def start_bot():
    global bot_thread, the_bot_should_continue_running
    bot_thread = Thread(target=intro, daemon=True)
    the_bot_should_continue_running = True  # before the `start` !
    bot_thread.start()
    return

def stop_bot():
    if bot_thread:
        global the_bot_should_continue_running
        the_bot_should_continue_running = False
        bot_thread.join()


if __name__ == "__main__":
    start_bot()  # simulating a request on it
    sleep(5.5)  # some time passes ...
    stop_bot()  # simulating a request on it

prints 6 times then exits.

  • Related