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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 22年12月更新:个人网站关停,如果仍旧对旧教程有兴趣参考 Github 的markdown内容[https://...
    tangyefei阅读 35,293评论 22 257
  • 本篇文章是基于谷歌有关Graphic的一篇概览文章的翻译:http://source.android.com/de...
    lee_3do阅读 11,977评论 2 21
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,314评论 19 139
  • cookie篇 cookie是客户端本地存储的一种方式,常见的客户端本地存储的方式还有:session stora...
    TheoLin阅读 1,378评论 0 1
  • 失城 ——写给LXX 慢慢的你把生活过成了老照片 路人甲乙都不能看出里面的浩劫 故事的梗概悬浮在你燃起的香烟上 浮...
    葱葱_阅读 2,669评论 10 12