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深入学习之内存管理
Aug 31 Python
解决python3中解压zip文件是文件名乱码的问题
Mar 22 Python
详解Django之admin组件的使用和源码剖析
May 04 Python
解决Python3.5+OpenCV3.2读取图像的问题
Dec 05 Python
python截取两个单词之间的内容方法
Dec 25 Python
python 通过SSHTunnelForwarder隧道连接redis的方法
Feb 19 Python
详解Python Opencv和PIL读取图像文件的差别
Dec 27 Python
python实现将json多行数据传入到mysql中使用
Dec 31 Python
Python如何实现后端自定义认证并实现多条件登陆
Jun 22 Python
Scrapy模拟登录赶集网的实现代码
Jul 07 Python
Python函数递归调用实现原理实例解析
Aug 11 Python
使用numpngw和matplotlib生成png动画的示例代码
Jan 24 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
Smarty Foreach 使用说明
2010/03/23 PHP
php判断一个数组是否为有序的方法
2015/03/27 PHP
一文看懂PHP进程管理器php-fpm
2020/06/01 PHP
js获取变量
2006/08/24 Javascript
PNG背景在不同浏览器下的应用
2009/06/22 Javascript
extjs 学习笔记(二) Ext.Element类
2009/10/13 Javascript
使用Math.floor与Math.random取随机整数的方法详解
2013/05/07 Javascript
文本框中禁止非数字字符输入比如手机号码、邮编
2013/08/19 Javascript
js下将阿拉伯数字每三位一逗号分隔(如:15000000转化为15,000,000)
2014/06/02 Javascript
jQuery实现冻结表格行和列
2015/04/29 Javascript
JavaScript实现给按钮加上双重动作的方法
2015/08/14 Javascript
javascript中tostring()和valueof()的用法及两者的区别
2015/11/16 Javascript
AngularJS  自定义指令详解及实例代码
2016/09/14 Javascript
微信小程序 教程之注册页面
2016/10/17 Javascript
js仿微信语音播放实现思路
2016/12/12 Javascript
Angular2里获取(input file)上传文件的内容的方法
2017/09/05 Javascript
JS中使用textPath实现线条上的文字
2017/12/25 Javascript
vue实现的上传图片到数据库并显示到页面功能示例
2018/03/17 Javascript
vue改变循环遍历后的数据实例
2019/11/07 Javascript
Vue中点击active并第一个默认选中功能的实现
2020/02/24 Javascript
JS中==、===你分清楚了吗
2020/03/04 Javascript
js 使用ajax设置和获取自定义header信息的方法小结
2020/03/12 Javascript
详解Python3.1版本带来的核心变化
2015/04/07 Python
实例讲解Python的函数闭包使用中应注意的问题
2016/06/20 Python
Django日志模块logging的配置详解
2017/02/14 Python
Python3.6通过自带的urllib通过get或post方法请求url的实例
2018/05/10 Python
Python实现的ftp服务器功能详解【附源码下载】
2019/06/26 Python
pyinstaller 3.6版本通过pip安装失败的解决办法(推荐)
2020/01/18 Python
解决python中显示图片的plt.imshow plt.show()内存泄漏问题
2020/04/24 Python
python字典的值可以修改吗
2020/06/29 Python
关于tensorflow softmax函数用法解析
2020/06/30 Python
英国电器零售商:PRC Direct
2018/06/21 全球购物
委托书格式范文
2015/01/28 职场文书
求职信格式范文
2015/03/19 职场文书
一篇文章带你复习java知识点
2021/06/28 Java/Android
MySQL索引失效十种场景与优化方案
2023/05/08 MySQL