使用 Flask 中的路由控制守护线程

control a daemon thread with routes in flask

如何在 python

中让两个路由控制一个守护线程

flask 后端文件

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

当试图终止线程时,终端中的 curl 请求不会 return 返回控制台并且线程继续向终端打印数据

我的想法是声明保存对 bot_thread 的引用的变量并使用路由来控制它
为了测试这一点,我使用了 curl http://localhost:port/startbotcurl http://localhost:port/stopbot

我可以很好地启动机器人,但是当我试图杀死它时,我得到以下信息

NameError: name 'bot_thread' is not defined

我们将不胜感激任何帮助和注意事项

考虑到在终止线程后,用户可以创建一个新线程,也可以终止它

这是一个 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

例如我的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

所以 :

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

但是

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

因此:

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

最终给出:

hello

编辑

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 bot_thread 运行 intro 函数。因为它包含一个无限循环 (while True),它永远不会到达函数的末尾(隐式 return),所以线程永远不会被视为已完成。正因为如此,当主线程尝试join(等到线程完成,然后得到结果)时,它会无休止地等待,因为bot线程卡在了循环中。
所以你必须让退出 while 循环成为可能。例如(就像我在评论中链接的示例),通过使用另一个 global 变量,一个 flag,它在主线程(路由 stop_bot)中设置并被选中在 intro 循环中。像这样:

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

打印 6 次然后退出。