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,224評論 22 257
  • 本篇文章是基于谷歌有關Graphic的一篇概覽文章的翻譯:http://source.android.com/de...
    lee_3do閱讀 7,182評論 2 21
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,947評論 18 139
  • cookie篇 cookie是客戶端本地存儲的一種方式,常見的客戶端本地存儲的方式還有:session stora...
    TheoLin閱讀 206評論 0 1
  • 失城 ——寫給LXX 慢慢的你把生活過成了老照片 路人甲乙都不能看出里面的浩劫 故事的梗概懸浮在你燃起的香煙上 浮...
    蔥蔥_閱讀 349評論 10 12