国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Flask嵌套啟動子線程如何讀取請求上下文?

開發 前端
網上的方法只能解決第一種寫法的問題。如果想使用方法2和方法3啟動子線程,代碼應該怎么寫呢?

如果你在Flask中啟動過子線程,然后在子線程中讀寫過g?對象或者嘗試從request?對象中讀取url參數,那么,你肯定對下面這個報錯不陌生:RuntimeError: Working outside of request context..

例如下面這段Flask代碼:

import threading
from flask import Flask, request

app = Flask(__name__)

def inner_func():
doc_id = request.args.get('doc_id', '')
print(f'用戶ID為:{doc_id}')


@app.route('/start_thread')
def start_thread():
thread = threading.Thread(target=inner_func)
thread.start()
return {'success': True, 'msg': '獲取用戶ID成功!'}

請求/start_thread接口就會報錯,如下圖所示:

圖片

如果你在網上搜索flask thread RuntimeError: Working outside of request context. ,那么你可能會看到官方文檔或者StackOverFlow上面提供了一個裝飾器@copy_current_request_context。如下圖所示:

圖片

照著它這樣寫,確實能解決問題,如下圖所示:

圖片

但無論是官網還是StackOverFlow,它的例子都非常簡單。但是我們知道,啟動線程有很多種方法,例如:

# 方法一,啟動簡單線程
import threading

job = threading.Thread(target=函數名, args=(參數1, 參數2), kwargs={'參數3': xxx, '參數4': yyy})
job.start()

# 方法2,使用類定義線程
import threading

class Job(threading.Thread):
def __init__(self, 參數):
super().__init__()

def run(self):
print('子線程開始運行')

job = Job(參數)
job.start()

# 方法3,使用線程池
from multiprocessing.dummy import Pool
pool = Pool(5) # 5個線程的線程池
pool.map(函數名, 參數列表)

網上的方法只能解決第一種寫法的問題。如果想使用方法2和方法3啟動子線程,代碼應該怎么寫呢?如果在子線程中又啟動子線程,再用一次@copy_current_request_context還行嗎?

相信我,你在網上搜索一下午,只有兩種結果:一是找不到答案,二是找到的答案是晚于2023年1月14日的,因為是別人看了我這篇文章以后,再寫的。

解答上面的問題前,還是說明一下我對于在后端啟動子線程這個行為的觀點。例如有些人喜歡在后端掛一個爬蟲,請求接口以后,通過線程啟動爬蟲,爬蟲開始爬數據。又或者,有些人在后端上面掛了一些復雜的程序代碼,請求接口以后,后端啟動子線程,在子線程中運行這些代碼。

我一向是不建議在后端又啟動子線程去做復雜操作的。無論你使用的是Flask還是Django還是FastAPI。正確的做法應該是使用消息隊列,后端只是把觸發任務的相關參數發送到消息隊列中。下游真正的運行程序從消息隊列讀取到觸發參數以后,開始運行。

但有時候,你可能綜合考慮性價比,覺得再增加一個消息隊列,成本太高;或者干脆是要趕工期,不得不先暫時使用多線程來解決問題,那么這篇文章將會極大幫助到你。

盡量不要在子線程中讀取請求相關的參數

如果你的子線程不需要讀寫g對象,也不需要從請求中讀取各種參數,那么你就可以關閉這篇文章了。因為你的子線程可以直接運行,不會遇到什么的問題,例如:

圖片

所以最好的解決方法,就是在啟動子線程之前,提前先獲取到子線程需要的每一個參數,然后把這些參數在啟動子線程的時候作為函數參數傳進去。如果你是從零開始寫代碼,那么一開始這樣做,就可以幫你避免很多麻煩。

但如果你是修改已有的代碼,并且嵌套太深,已經沒有辦法一層一層傳入參數,或者代碼量太大,不知道哪些地方悄悄調用了g對象或者讀寫了請求上下文,那么你可以繼續往下看。

裝飾閉包函數而不是一級函數

上面的簡單多線程寫法,有一個地方需要特別注意,被@copy_current_request_context裝飾的子線程入口函數inner_func,必須是閉包函數,不能是一級函數。如下圖所示:

圖片

如果不小心裝飾了一級函數,就會報如下的錯誤:

圖片

線程池復制請求上下文

當我們使用multiprocessing.dummy來實現線程池時,代碼如下:

from multiprocessing.dummy import Pool
from flask import Flask, request, copy_current_request_context, g

app = Flask(__name__)


@app.route('/start_thread', methods=['POST'])
def start_thread():
@copy_current_request_context
def crawl(doc_id):
url_template = request.json.get('url_template', '')
url = url_template.format(doc_id=doc_id)
print(f'開始爬取:{url}')

doc_id_list = [123, 456, 789, 111, 222, 333, 444]
pool = Pool(3)
pool.map(crawl, doc_id_list)
return {'success': True, 'msg': '爬取文章成功!'}

運行效果如下圖所示:

圖片

寫法上整體跟threading.Thread啟動簡單線程的方法差不多。

用類定義線程時復制請求上下文

當我們額外定義了一個線程類時,需要把被裝飾的閉包函數傳入到子線程中,然后在子線程的run()方法中運行:

import threading
from flask import Flask, request, copy_current_request_context

app = Flask(__name__)


class Job(threading.Thread):
def __init__(self, func):
super().__init__()
self.func = func

def run(self):
self.func()


@app.route('/start_thread', methods=['POST'])
def start_thread():
@copy_current_request_context
def runner():
doc_id = request.json.get('doc_id', '')
print(f'docId的值是:{doc_id}')
job = Job(runner)
job.start()
return {'success': True, 'msg': '讀取文章成功!'}

運行效果如下圖所示:

圖片

嵌套子線程復制請求上下文

有時候,我們先創建了一個子線程,然后在子線程中,又需要創建孫線程。并且在孫線程中讀取請求上下文。例如下面的代碼:

import threading
from multiprocessing.dummy import Pool
from flask import Flask, request, copy_current_request_context

app = Flask(__name__)


def deep_func_runner(doc_id_list):
@copy_current_request_context
def deep_func(doc_id):
category = request.args.get('category', '')
url = f'https://www.kingname.info/{category}/{doc_id}'
print(f'開始爬取:{url}')
pool = Pool(3)
pool.map(deep_func, doc_id_list)


@app.route('/start_thread', methods=['POST'])
def start_thread():
@copy_current_request_context
def runner():
doc_id_list = [111, 222, 333, 444, 555, 666, 777, 888, 999]
deep_func_runner(doc_id_list)

job = threading.Thread(target=runner)
job.start()
return {'success': True, 'msg': '讀取文章成功!'}

此時使用@copy_current_request_context就會報您一個錯誤:ValueError: <Token var=<ContextVar name='flask.request_ctx' at 0x103ef69a0> at 0x104446700> was created in a different Context。如下圖所示:

圖片

這個時候,我們就需要額外再創建一個裝飾器:

def copy_current_app_context(f):
from flask.globals import _app_ctx_stack
appctx = _app_ctx_stack.top
def _(*args, **kwargs):
with appctx:
return f(*args, **kwargs)
return _

@copy_current_app_context這個裝飾器需要放到孫線程里面@copy_current_request_context的上面。完整的代碼為:

import threading
from multiprocessing.dummy import Pool
from flask import Flask, request, copy_current_request_context

app = Flask(__name__)

def copy_current_app_context(f):
from flask.globals import _app_ctx_stack
appctx = _app_ctx_stack.top
def _(*args, **kwargs):
with appctx:
return f(*args, **kwargs)
return _


def deep_func_runner(doc_id_list):
@copy_current_app_context
@copy_current_request_context
def deep_func(doc_id):
category = request.args.get('category', '')
url = f'https://www.kingname.info/{category}/{doc_id}'
print(f'開始爬取:{url}')
pool = Pool(3)
pool.map(deep_func, doc_id_list)


@app.route('/start_thread', methods=['POST'])
def start_thread():
@copy_current_request_context
def runner():
doc_id_list = [111, 222, 333, 444, 555, 666, 777, 888, 999]
deep_func_runner(doc_id_list)

job = threading.Thread(target=runner)
job.start()
return {'success': True, 'msg': '讀取文章成功!'}

運行效果如下圖所示,孫線程也正常啟動了:

圖片

總結

  1. 非必要不在后端中創建子線程
  2. 創建子線程時,如果能把參數從外面傳入,就不要讓子線程自己去Flask的上下文讀取
  3. @copy_current_request_context需要裝飾閉包函數,不能裝飾一級函數
  4. 嵌套子線程需要同時使用@copy_current_app_context和@copy_current_request_context兩個裝飾器來裝飾孫線程的閉包函數

責任編輯:武曉燕 來源: 未聞Code
相關推薦

2017-05-11 14:00:02

Flask請求上下文應用上下文

2022-09-14 13:13:51

JavaScript上下文

2022-09-15 08:01:14

繼承基礎設施基礎服務

2012-12-31 10:01:34

SELinuxSELinux安全

2024-03-14 08:11:45

模型RoPELlama

2023-06-28 08:08:06

Flask上下文生命周期

2021-05-09 21:50:48

項目實踐上下文

2025-12-09 09:28:54

2023-05-04 12:55:04

用戶界面對象線程

2025-04-03 07:33:56

2023-07-11 10:02:23

2022-10-28 16:24:33

Context上下文鴻蒙

2017-12-17 17:01:23

限界上下文系統模型

2024-09-30 14:10:00

2025-12-08 02:35:00

上下文工程系統AI

2025-03-18 08:14:05

2025-09-09 09:49:36

2020-07-24 10:00:00

JavaScript執行上下文前端

2021-07-26 07:47:36

Cpu上下文進程

2025-06-06 08:00:00

上下文管理器Python開發
點贊
收藏

51CTO技術棧公眾號

日韩porn| 91久久久久久久久久久| 国产精品久久久久久久av电影| 国产精品免费一区二区三区四区| 亚洲精品在线观看免费| 青青草免费在线视频| 琪琪久久久久日韩精品| 国产精品天干天干在观线| 亚洲国产女人aaa毛片在线| 免费看污久久久| 国产黄色片在线播放| 狠狠久久婷婷| 欧美日韩一卡二卡三卡| 日韩在线视频导航| 91精品国产一区二区三区动漫| 伊人资源视频在线| 免费成人小视频| 992tv在线观看免费进| 久草在线资源福利站| 亚洲va韩国va欧美va| 天堂男人av| 国产精品美女久久久| 日韩精品电影在线观看| 国产精品乱人伦| 亚洲国产天堂久久综合| 亚洲第一综合| 在线观看a视频| 久久这里只有精品6| 成人精品一区二区三区电影免费| 久操av在线| 一区二区三区免费在线看| 66视频精品| 五月天婷婷综合| 午夜老司机精品| 国产真实生活伦对白| 91青青在线视频| 一区二区网站| 久久国产精品色婷婷| 一区二区三区在线视频观看58| 欧美视频中文一区二区三区在线观看| 久久久亚洲国产天美传媒修理工| 欧美aaa一级片| 秋霞成人午夜伦在线观看| h视频久久久| 成人av网在线| 国产成人精品福利一区二区三区| 一级片在线播放| www.亚洲色图.com| 色之综合天天综合色天天棕色 | 九九久久国产精品| 国内精品伊人久久久久av影院| 91在线短视频| 久久欧美肥婆一二区| 国内av免费| 91福利国产成人精品照片| 猫咪成人官网| thepron国产精品| www.男人天堂网| 综合欧美亚洲| 久久国产精品久久久久久| 免费在线超碰| 美女在线观看视频一区二区| 日本欧美爱爱爱| 先锋资源久久| 日韩偷拍一区二区| 欧美大黑bbbbbbbbb在线| 久久精品美女视频网站| 精品极品在线| 在线综合+亚洲+欧美中文字幕| 国产呻吟对白刺激无套视频在线| 久久久国产精品不卡| www.日本xxxx| 日韩欧美一卡二卡| 欧美hdxxx| 国内精品模特av私拍在线观看| 日本午夜精品久久久| 国产精品羞羞答答| 欧美高清一级片| 欧美国产日韩一区二区| 久久经典视频| 欧美韩国一区二区| 国产精品久久久久久久av电影| 国产一区二区三区| 亚洲欧美精品中文字幕在线| 日韩和的一区二在线| 亚洲第一视频在线观看| 青青草伊人久久| 人人澡人人爽| 国产精品成人网| av在线电影院| 精品国产一区二区三区忘忧草| 一二三四社区在线视频6| 一区二区三区四区在线观看视频| 秋霞电影一区二区| 免费h片在线观看| 亚洲国产欧美日韩另类综合| 久久综合伊人77777麻豆| 91国产免费看| 日皮视频在线观看| 国产一区二区久久精品| 亚洲国产天堂| 精品久久久久久中文字幕大豆网| 捆绑调教日本一区二区三区| 在线日韩精品视频| 国产精品video| 国产综合色视频| av网页在线| 国产精品视频一区二区三区四| 国产一区不卡精品| 男人天堂2020| 久久精品视频在线播放| 日韩亚洲在线视频| 欧美片网站yy| 国产精品久久久乱弄| 992kp快乐看片永久免费网址| 精品美女一区二区| 久久av一区| 国产精品一二三区视频| 全亚洲最色的网站在线观看| 国产精品一区二区视频| 国产在线拍揄自揄拍视频| 91香蕉电影院| 这里只有精品99re| 国产一区二区三区四| 久久久久久久性潮| 亚洲黄色av网址| 韩国19禁主播vip福利视频| 国产精品人人做人人爽人人添| 中文字幕亚洲在线观看| 中文字幕视频在线免费观看| 欧洲精品在线视频| 亚洲激情欧美激情| 偷拍一区二区| 九一在线视频| 亚洲欧美日韩爽爽影院| 日韩午夜三级在线| 久久精品盗摄| 夜鲁夜鲁夜鲁视频在线播放| 九色自拍视频在线观看| 欧美最顶级的aⅴ艳星| 偷拍日韩校园综合在线| 大胆人体一区| 日本免费一区二区三区等视频| 国产精品丝袜久久久久久高清| 一区二区三区亚洲| 欧美大胆视频| 亚洲精品.com| 成人福利视频在| 国产亚洲视频中文字幕视频| 亚洲国产精品va在线看黑人| 成人av资源网站| 日本在线视频一区二区三区| 国产在线98福利播放视频| 亚洲品质视频自拍网| 综合av第一页| jvid福利写真一区二区三区| 澳门精品久久国产| 久久99蜜桃综合影院免费观看| 3d动漫啪啪精品一区二区免费 | 久久精品日产第一区二区三区精品版| 中日韩美女免费视频网址在线观看| 亚洲男人天堂av| 美女视频黄 久久| 国产精品欧美一区二区三区不卡| 日本新janpanese乱熟| 欧美日韩在线高清| 九九精品视频在线| 成人福利电影| 97视频免费看| 色综合久久精品亚洲国产| 国产精品美女久久| 水蜜桃亚洲精品| 91精品国产九九九久久久亚洲| 久久美女艺术照精彩视频福利播放| 亚洲免费av高清| 精品美女一区二区| 欧美精品福利视频| 国产偷国产偷亚洲清高网站| 亚洲国产精品久久91精品| 日本韩国欧美国产| 日韩精品一区二区三区swag| 日韩精品在线免费播放| 少妇激情综合网| 日韩女优av电影| 精品久久国产老人久久综合| 久久精品视频免费播放| 久久国产视频网站| 97超级碰在线看视频免费在线看| 日韩精品在线看| 91高清免费在线观看| 麻豆久久久9性大片| 精品欧美日韩| 亚洲国产高清av| 日本午夜激情视频| 成品人视频ww入口| 欧美精品亚洲| 少妇人妻在线视频| 日本三级在线播放完整版| 亚洲人成77777男人| 日本亚洲欧美|