这篇文章主要介绍了Django异步任务线程池实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
当数据库数据量很大时(百万级),许多批量数据修改请求的响应会非常慢,一些不需要即时响应的任务可以放到后台的异步线程中完成,发起异步任务的请求就可以立即响应
选择用线程池的原因是:线程比进程更为可控。不像子进程,子线程会在所属进程结束时立即结束。线程可共享内存。
请求任务异步处理的原理
使用python manage.py runserver模式启动的Django应用只有一个进程,对于每个请求,主线程会开启一个子线程来处理请求。请求子线程向主线程申请一个新线程,然后把耗时的任务交给新线程,自身立即响应,这就是请求任务异步处理的原理。
可视化线程池
如果想要管理这批异步线程,知道他们是否在运行中,可以使用线程池(ThreadPoolExecutor)。
线程池会先启动若干数量的线程,并让这些线程都处于睡眠状态,当向线程池submit一个任务后,会唤醒线程池中的某一个睡眠线程,让它来处理这个任务,当处理完这个任务,线程又处于睡眠状态。
submit任务后会返回一个期程(future),这个对象可以查看线程池中执行此任务的线程是否仍在处理中
因此可以构建一个全局可视化线程池:
from concurrent.futures.thread import ThreadPoolExecutor class ThreadPool(object): def __init__(self): # 线程池 self.executor = ThreadPoolExecutor(20) # 用于存储每个项目批量任务的期程 self.future_dict = {} # 检查某个项目是否有正在运行的批量任务 def is_project_thread_running(self, project_id): future = self.future_dict.get(project_id, None) if future and future.running(): # 存在正在运行的批量任务 return True return False # 展示所有的异步任务 def check_future(self): data = {} for project_id, future in self.future_dict.items(): data[project_id] = future.running() return data def __del__(self): self.executor.shutdown() # 主线程中的全局线程池 # global_thread_pool的生命周期是Django主线程运行的生命周期 global_thread_pool = ThreadPool()
使用:
# 检查异步任务 if global_thread_pool.is_project_thread_running(project_id): raise exceptions.ValidationError(detail='存在正在处理的批量任务,请稍后重试') # 提交一个异步任务 future = global_thread_pool.executor.submit(self.batch_thread, project_id) global_thread_pool.future_dict[project_id] = future # 查看所有异步任务 @login_required def check_future(request): data = global_thread_pool.check_future() return HttpResponse(status=status.HTTP_200_OK, content=json.dumps(data))
串行执行
使用线程锁
在全局线程池中初始化线程锁
class ThreadPool(object): def __init__(self): self.executor = ThreadPoolExecutor(20) self.future_dict = {} self.lock = threading.Lock()
然后执行线程前需要获取锁并再执行结束后释放锁
def batch_thread(self): global_thread_pool.lock.acquire() try: ... global_thread_pool.lock.release() except Exception: trace_log = traceback.format_exc() logger.error('异步任务执行失败:\n %s' % trace_log) global_thread_pool.lock.release()
需要捕捉异常预防子线程出错而无法释放锁的情况
异步线程任务执行前先检查数据库连接是否可用,然后关掉不可用连接
由于django的数据库连接是保存到线程本地变量中的,通过ThreadPoolExecutor创建的线程会保存各自的数据库连接。
当连接被保存的时间超过mysql连接的最大超时时间,连接失效,但不会被线程释放。
之后再调起线程执行涉及到数据库操作的异步任务时,会用到失效的数据库连接,导致报错“MySQL server has gone away”。
解决方案是在线程池的所有异步任务执行前先检查数据库连接是否可用,然后关掉不可用连接
def batch_thread(self): for conn in connections.all(): conn.close_if_unusable_or_obsolete() ...
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
Django,异步,任务,线程池
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]