• Python3关于current_app传递给子线程


    在学习Flask的时候,《Flask Web开发》这本书中有一个异步发送email的例子,
    其中用到了线程

    from . import mail,create_app
    
    
    def send_async_email(app,msg):
        with app.app_context():
            mail.send(msg)
    
    def send_email(to,subject,template,**kwargs):
        msg = Message(current_app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
                      sender=current_app.config['FLASKY_MAIL_SENDER'], recipients=[to])
        msg.body = render_template(template + '.txt', **kwargs)
        msg.html = render_template(template + '.txt', **kwargs)
        #with current_app.app_context():
        #    mail.send(msg)
        thr = Thread(target=send_async_email, args=(current_app, msg))
        thr.start()
        return thr

    发送邮件总是提示错误
    RuntimeError: Working outside of application context.
    后来查找资料才知道是传递current_app的问题

    current_app在 Flask是一个代理,如果你看 Flask源码的话会发现其实它外部包裹的是这样的:

    def _find_app():
        top = _app_ctx_stack.top
        if top is None:
            raise RuntimeError(_app_ctx_err_msg)
        return top.app
    
    ...
    
    current_app = LocalProxy(_find_app)

    这个 LocalProxy就不展开讲了,但是我可以告诉你这个LocalProxy的作用就是可以根据线程/协程返回对应当前协程/线程的对象,也就是说
    线程 A 往 LocalProxy 中塞入 A
    线程 B 往 LocalProxy 中塞入 B
    无论在是什么地方,线程 A 永远取到得是 A,线程 B 取到得永远是 B

    这就是在 Flask中可以在代码中直接使用 request、current_app这样的变量的底层原因。
    所以,因为这里开了一个新线程,如果你不传真实对象过去,那么你在线程里面使用 current_app将获取不到对象,因为他没有 flask 上下文。

    获取真实对象Flask提供了一个方法:
    _get_current_object()
    官方文档是这样解释:

    Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context.

    修改send_email函数后代码如下:

    def send_email(to,subject,template,**kwargs):
        msg = Message(current_app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
                      sender=current_app.config['FLASKY_MAIL_SENDER'], recipients=[to])
        msg.body = render_template(template + '.txt', **kwargs)
        msg.html = render_template(template + '.txt', **kwargs)
        #with current_app.app_context():
        #    mail.send(msg)
        #current_app只是一个代理获取而已,传递给其它子线程获取到的依然是子线程的上下文
        # 必须_get_current_object线获取到原始对象再传递过去
        app = current_app._get_current_object()
        thr = Thread(target=send_async_email, args=(app, msg))
        thr.start()
        return thr

    这样就能异步发送邮件成功了

    转自:https://www.jianshu.com/p/acaf646f537c

  • 相关阅读:
    课后作业一
    软工假期预习作业
    Spark2.0自定义累加器
    JAVA hashmap知识整理
    Spark 累加器
    RDD与DataFrame的转换
    Scala笔记整理
    Spark RDD
    Hbase的flush机制
    Spring源码-ImportSelector实现分析
  • 原文地址:https://www.cnblogs.com/aidenzdly/p/11127597.html
Copyright © 2020-2023  润新知