简体   繁体   English

为什么子线程不能访问 flask_login 中的 current_user 变量?

[英]Why child threads cannot access the current_user variable in flask_login?

I am writing a Flask application and I am trying to insert a multi-threaded implementation for certain server related features.我正在编写一个Flask 应用程序,我正在尝试为某些与服务器相关的功能插入一个多线程实现 I noticed this weird behavior so I wanted to understand why is it happening and how to solve it.我注意到了这种奇怪的行为,所以我想了解为什么会发生这种情况以及如何解决它。 I have the following code:我有以下代码:

from flask_login import current_user, login_required
import threading

posts = Blueprint('posts', __name__)

@posts.route("/foo")
@login_required
def foo():
    print(current_user)
    thread = threading.Thread(target=goo)
    thread.start()
    thread.join()
    return


def goo():
    print(current_user)
    # ...

The main process correctly prints the current_user , while the child thread prints None .主进程正确打印current_user ,而子线程打印None

User('Username1', 'email1@email.com', 'Username1-ProfilePic.jpg')
None

Why is it happening?为什么会这样? How can I manage to obtain the current_user also in the child process?我怎样才能设法在子进程中也获得current_user I tried passing it as argument of goo but I still get the same behavior.我尝试将它作为goo的参数传递,但我仍然得到相同的行为。

I found this post but I can't understand how to ensure the context is not changing in this situation, so I tried providing a simpler example.我找到了这篇文章,但我无法理解如何确保上下文在这种情况下不会发生变化,因此我尝试提供一个更简单的示例。

A partially working workaround部分工作的解决方法

I tried passing as parameter also a newly created object User populated with the data from current_user我尝试将新创建的 object 用户作为参数传递,该User填充了来自current_user的数据

def foo():
    # ...
    user = User.query.filter_by(username=current_user.username).first_or_404()
    thread = threading.Thread(target=goo, args=[user])
    # ...

def goo(user):
    print(user)
    # ...

And it correctly prints the information of the current user.并正确打印出当前用户的信息。 But since inside goo I am also performing database operations I get the following error:但是由于在goo内部我也在执行数据库操作,所以我收到以下错误:

RuntimeError: No application found.运行时错误:找不到应用程序。 Either work inside a view function or push an application context.在视图 function 内工作或推送应用程序上下文。 See http://flask-sqlalchemy.pocoo.org/contexts/ .请参见http://flask-sqlalchemy.pocoo.org/contexts/

So as I suspected I assume it's a problem of context.因此,正如我所怀疑的那样,我认为这是上下文问题。

I tried also inserting this inside goo as suggested by the error:我也尝试按照错误的建议将其插入goo中:

def goo():
    from myapp import create_app
    app = create_app()
    app.app_context().push()
    # ... database access

But I still get the same errors and if I try to print current_user I get None .但是我仍然遇到相同的错误,如果我尝试打印current_user我得到None

How can I pass the old context to the new thread?如何将旧上下文传递给新线程? Or should I create a new one?还是我应该创建一个新的?

This is because Flask uses thread local variables to store this for each request's thread.这是因为 Flask 使用线程局部变量为每个请求的线程存储它。 That simplifies in many cases, but makes it hard to use multiple threads.这在很多情况下都简化了,但很难使用多线程。 Seehttps://flask.palletsprojects.com/en/1.1.x/design/#thread-local .请参阅https://flask.palletsprojects.com/en/1.1.x/design/#thread-local

If you want to use multiple threads to handle a single request, Flask might not be the best choice.如果您想使用多个线程来处理单个请求,Flask 可能不是最佳选择。 You can always interact with Flask exclusively on the initial thread if you want and then forward anything you need on other threads back and forth yourself through a shared object of some kind.如果你愿意,你总是可以在初始线程上与 Flask 进行专门的交互,然后通过某种共享的 object 自己来回转发你在其他线程上需要的任何东西。 For database access on secondary threads, you can use a thread-safe database library with multiple threads as long as Flask isn't involved in its usage.对于辅助线程上的数据库访问,只要 Flask 不涉及其使用,您就可以使用具有多线程的线程安全数据库库。

In summary, treat Flask as single threaded.综上所述,将 Flask 视为单线程。 Any extra threads shouldn't interact directly with Flask to avoid problems.任何额外的线程不应直接与 Flask 交互以避免出现问题。 You can also consider either not using threads at all and run everything sequentially or trying eg Tornado and asyncio for easier concurrency with coroutines depending on the needs.您还可以考虑根本不使用线程并按顺序运行所有内容,或者尝试使用 Tornado 和 asyncio 等,以便根据需要更轻松地与协程并发。

your server serves multiple users, wich are threads by themself.您的服务器为多个用户提供服务,它们本身就是线程。 flask_login was not designed for extra threading in it, thats why child thread prints None. flask_login 不是为其中的额外线程而设计的,这就是子线程打印 None 的原因。

i suggest u to use db for transmit variables from users and run addition docker container if you need separate process.如果您需要单独的进程,我建议您使用 db 来传输来自用户的变量并运行 addition docker 容器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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