Django异步任务线程池实现原理


Posted in Python onDecember 17, 2019

这篇文章主要介绍了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()
  ...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python socket C/S结构的聊天室应用实现
Nov 30 Python
Python中实现结构相似的函数调用方法
Mar 10 Python
Python比较两个图片相似度的方法
Mar 13 Python
Python随机生成一个6位的验证码代码分享
Mar 24 Python
python编码总结(编码类型、格式、转码)
Jul 01 Python
Python遍历文件夹和读写文件的实现代码
Aug 28 Python
python实现八大排序算法(2)
Sep 14 Python
Python发送http请求解析返回json的实例
Mar 26 Python
谈谈Python:为什么类中的私有属性可以在外部赋值并访问
Mar 05 Python
Tensorflow与Keras自适应使用显存方式
Jun 22 Python
python数据抓取3种方法总结
Feb 07 Python
利用Python实现翻译HTML中的文本字符串
Jun 21 Python
python 求10个数的平均数实例
Dec 16 #Python
python 经典数字滤波实例
Dec 16 #Python
Python实现把类当做字典来访问
Dec 16 #Python
python中p-value的实现方式
Dec 16 #Python
基于python读取.mat文件并取出信息
Dec 16 #Python
python基于plotly实现画饼状图代码实例
Dec 16 #Python
python 实现让字典的value 成为列表
Dec 16 #Python
You might like
关于PHP结束标签的使用细节探讨及联想
2013/03/04 PHP
分享ThinkPHP3.2中关联查询解决思路
2015/09/20 PHP
PHP实现通过strace定位故障原因的方法
2018/04/29 PHP
laravel实现Auth认证,登录、注册后的页面回跳方法
2019/09/30 PHP
获取页面高度,窗口高度,滚动条高度等参数值getPageSize,getPageScroll
2006/09/22 Javascript
解决 firefox 不支持 document.all的方法
2007/03/12 Javascript
jQuery 源码分析笔记(6) jQuery.data
2011/06/08 Javascript
JQuery的AJAX实现文件下载的小例子
2013/05/15 Javascript
JS格式化数字金额用逗号隔开保留两位小数
2013/10/18 Javascript
JQGrid的用法解析(列编辑,添加行,删除行)
2013/11/08 Javascript
原生js事件的添加和删除的封装
2014/07/01 Javascript
jQuery取得设置清空select选择的文本与值
2014/07/08 Javascript
ztree获取当前选中节点子节点id集合的方法
2015/02/12 Javascript
JavaScript 常见安全漏洞和自动化检测技术
2015/08/21 Javascript
MVC+jQuery.Ajax异步实现增删改查和分页
2020/12/22 Javascript
详解angularjs结合pagination插件实现分页功能
2017/02/10 Javascript
详解从零搭建 vue2 vue-router2 webpack3 工程
2017/11/22 Javascript
使用express来代理服务的方法
2019/06/21 Javascript
Vue 数据响应式相关总结
2021/01/28 Vue.js
[02:42]完美大师赛主赛事淘汰赛第三日观众采访
2017/11/25 DOTA
在Python编程过程中用单元测试法调试代码的介绍
2015/04/02 Python
Python使用pip安装pySerial串口通讯模块
2018/04/20 Python
解决ROC曲线画出来只有一个点的问题
2020/02/28 Python
Python调用shell cmd方法代码示例解析
2020/06/18 Python
解决Keras中CNN输入维度报错问题
2020/06/29 Python
Python编写万花尺图案实例
2021/01/03 Python
css3边框_动力节点Java学院整理
2017/07/11 HTML / CSS
关于HTML5的22个初级技巧(图文教程)
2012/06/21 HTML / CSS
法国票务网站:Ticketmaster法国
2018/07/09 全球购物
药剂专业求职信
2014/06/20 职场文书
城市轨道交通工程职业生涯规划书范文
2014/09/16 职场文书
乡镇党的群众路线教育实践活动个人对照检查材料
2014/09/23 职场文书
学校教代会开幕词
2016/03/04 职场文书
Golang二维切片初始化的实现
2021/04/08 Golang
详解Vue的列表渲染
2021/11/20 Vue.js
vue二维数组循环嵌套方式 循环数组、循环嵌套数组
2022/04/24 Vue.js