Почему дочерние потоки не могут получить доступ к переменной current_user в flask_login?

Я пишу приложение Flask и пытаюсь вставить многопоточную реализацию для определенных функций, связанных с сервером. Я заметил это странное поведение, поэтому хотел понять, почему это происходит и как с этим справиться. У меня такой код:

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)
    # ...

Основной процесс правильно печатает current_user, а дочерний поток - None.

User('Username1', '[email protected]', 'Username1-ProfilePic.jpg')
None

Почему это происходит? Как я могу получить current_user также в дочернем процессе? Я попытался передать его как аргумент goo, но по-прежнему получаю такое же поведение.

Я нашел этот пост, но не могу понять, как гарантировать, что контекст не меняется в этом ситуации, поэтому я попытался привести более простой пример.

Частично рабочий обходной путь

Я попытался передать в качестве параметра также недавно созданный объект 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)
    # ...

И он правильно печатает информацию о текущем пользователе. Но поскольку внутри goo я также выполняю операции с базой данных, я получаю следующую ошибку:

RuntimeError: приложение не найдено. Либо работайте внутри функции просмотра, либо проталкивайте контекст приложения. См. http://flask-sqlalchemy.pocoo.org/contexts/.

Итак, как я и подозревал, я предполагаю, что это проблема контекста.

Я попытался также вставить это внутрь goo, как подсказала ошибка:

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

Но я все равно получаю те же ошибки, и если я пытаюсь напечатать current_user, я получаю None.

Как передать старый контекст новому потоку? Или создать новый?


person Robb1    schedule 10.10.2020    source источник
comment
Вы пробовали рекомендации по этой ссылке? Например, с использованием with app.app_context():.   -  person Cihan    schedule 10.10.2020
comment
Я новичок в фреймворке, поэтому не знаю, правильно ли я поступаю, но я попытался вложить все внутри goo в оператор with create_app().app_context():, и поведение осталось прежним. (Я использовал create_app(), поскольку использую Blueprints, у меня нет прямой ссылки на app, но у меня такое чувство, что то, что я сделал, было совершенно неправильным)   -  person Robb1    schedule 10.10.2020
comment
@Cihan Я добавил ответ на ваш вопрос в исходном посте. Еще раз спасибо за поддержку!   -  person Robb1    schedule 10.10.2020
comment
Хм, я думал, у вас уже есть приложение, описанное в этом документе. В этом случае я недостаточно знаю о Flask / Sqlalchemy, чтобы комментировать дальше. Удачи! :)   -  person Cihan    schedule 10.10.2020


Ответы (3)


Это связано с тем, что Flask использует локальные переменные потока для хранения этого для каждого потока запроса. Это упрощает во многих случаях, но затрудняет использование нескольких потоков. См. https://flask.palletsprojects.com/en/1.1.x/design/#thread-local.

Если вы хотите использовать несколько потоков для обработки одного запроса, Flask может быть не лучшим выбором. Вы всегда можете взаимодействовать с Flask исключительно в исходном потоке, если хотите, а затем перенаправлять все, что вам нужно, в другие потоки вперед и назад через какой-то общий объект. Для доступа к базе данных во вторичных потоках вы можете использовать поточно-ориентированную библиотеку базы данных с несколькими потоками, если Flask не участвует в ее использовании.

Таким образом, относитесь к Flask как к однопоточному. Любые дополнительные потоки не должны напрямую взаимодействовать с Flask, чтобы избежать проблем. Вы также можете подумать о том, чтобы либо вообще не использовать потоки и запускать все последовательно, либо пытаться, например. Tornado и asyncio для упрощения параллелизма с сопрограммами в зависимости от потребностей.

person Hampus    schedule 19.10.2020

ваш сервер обслуживает нескольких пользователей, которые сами по себе являются потоками. flask_login не был предназначен для дополнительной потоковой передачи в нем, поэтому дочерний поток печатает None.

Я предлагаю вам использовать db для передачи переменных от пользователей и запустить дополнительный контейнер докеров, если вам нужен отдельный процесс.

person Evgene    schedule 15.10.2020

Это потому, что current_user реализован как локальный безопасный ресурс: https://github.com/maxcountryman/flask-login/blob/main/flask_login/utils.py#L26

Прочтите: https://werkzeug.palletsprojects.com/en/1.0.x/local/#module-werkzeug.local

person pbacterio    schedule 21.10.2020