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 相关文章推荐
解决pandas 作图无法显示中文的问题
May 24 Python
Python实现按逗号分隔列表的方法
Oct 23 Python
python针对不定分隔符切割提取字符串的方法
Oct 26 Python
Python中文件的写入读取以及附加文字方法
Jan 23 Python
基于python的socket实现单机五子棋到双人对战
Mar 24 Python
Django 在iframe里跳转顶层url的例子
Aug 21 Python
解决Django Haystack全文检索为空的问题
May 19 Python
python属于解释型语言么
Jun 15 Python
Django如何使用asyncio协程和ThreadPoolExecutor多线程
Oct 12 Python
Python txt文件如何转换成字典
Nov 03 Python
用OpenCV进行年龄和性别检测的实现示例
Jan 29 Python
Python 实现劳拉游戏的实例代码(四连环、重力四子棋)
Mar 03 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
帅气的琦玉老师
2020/03/02 日漫
PHP中使用FFMPEG获取视频缩略图和视频总时长实例
2014/05/04 PHP
Windows下的PHP安装pear教程
2014/10/24 PHP
yii实现model添加默认值的方法(2种方法)
2016/01/06 PHP
php制作圆形用户头像的实例_自定义封装类源代码
2017/09/18 PHP
div层的移动及性能优化
2010/11/16 Javascript
jQuery powerFloat万能浮动层下拉层插件使用介绍
2010/12/27 Javascript
精通Javascript系列之数据类型 字符串
2011/06/08 Javascript
JSON 对象未定义错误的解决方法
2016/09/29 Javascript
深入浅析Nodejs的Http模块
2017/06/20 NodeJs
jquery实现侧边栏左右伸缩效果的示例
2017/12/19 jQuery
让axios发送表单请求形式的键值对post数据的实例
2018/08/11 Javascript
vue+axios+mock.js环境搭建的方法步骤
2018/08/28 Javascript
node.js使用express框架进行文件上传详解
2019/03/03 Javascript
vue配置接口域名方法总结
2019/05/12 Javascript
jQuery实现图片随机切换、抽奖功能(实例代码)
2019/10/23 jQuery
vue项目出现页面空白的解决方案
2019/10/31 Javascript
微信小程序scroll-view点击项自动居中效果的实现
2020/03/25 Javascript
JavaScript进阶(二)词法作用域与作用域链实例分析
2020/05/09 Javascript
[06:44]2014DOTA2国际邀请赛-钥匙体育馆开战 开幕式振奋人心
2014/07/19 DOTA
Python中的zipfile模块使用详解
2015/06/25 Python
Flask模拟实现CSRF攻击的方法
2018/07/24 Python
Python实现从SQL型数据库读写dataframe型数据的方法【基于pandas】
2019/03/18 Python
基于 Django 的手机管理系统实现过程详解
2019/08/16 Python
Python底层封装实现方法详解
2020/01/22 Python
xadmin使用formfield_for_dbfield函数过滤下拉表单实例
2020/04/07 Python
浅析Python 简单工厂模式和工厂方法模式的优缺点
2020/07/13 Python
解决python3中os.popen()出错的问题
2020/11/19 Python
python如何发送带有附件、正文为HTML的邮件
2021/02/27 Python
安踏官方商城:anta.cn
2019/12/16 全球购物
C++是不是类型安全的
2014/02/18 面试题
职专应届生求职信
2013/11/16 职场文书
应届大学生的推荐信
2013/11/20 职场文书
重阳节活动总结
2014/08/27 职场文书
长城导游词400字
2015/01/30 职场文书
2015年教师学期工作总结
2015/04/30 职场文书