当数据库数据量很大时(百万级),许多批量数据修改请求的响应会非常慢,一些不需要即时响应的任务可以放到后台的异步线程中完成,发起异步任务的请求就可以立即响应

选择用线程池的原因是:线程比进程更为可控。不像子进程,子线程会在所属进程结束时立即结束。线程可共享内存。

请求任务异步处理的原理

使用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异步任务线程池的更多相关文章

  1. JDK 伪异步编程(线程池)

    伪异步IO编程 BIO主要的问题在于每当有一个新的客户端请求接入时,服务端必须创建一个新的线程处理新接入的客户端链路,一个线程只能处理一个客户端连接.在高性能服务器应用领域,往往需要面向成千上万个客户 ...

  2. Java异步、线程池解决方案

    一.ThreadPoolExecutor------线程池 private static final ThreadPoolExecutor threadPoolExecutor = new Threa ...

  3. JAVA并行异步编程,线程池+FutureTask

    java 在JDK1.5中引入一个新的并发包java.util.concurrent 该包专门为java处理并发而书写. 在java中熟悉的使用多线程的方式为两种?继续Thread类,实现Runnal ...

  4. SpringBoot异步及线程池配置

    异步方法注解@Async 在SpringBoot中进行异步处理,可以使用异步注解@Async和@EnableAsync. @Async注解表示异步,如:@Async("asyncServic ...

  5. 【mq读书笔记】客户端处理消息(回调提交到异步业务线程池,pullRequest重新入队)

    看一下客户端收到消息后的处理: MQClientAPIImpl#processPullResponse private PullResult processPullResponse( final Re ...

  6. 个人开源项目之异步Http线程池框架

    项目开源于:https://github.com/HouZhiHouJue/AsyncHttpThreadPool 示意图:

  7. SpringBoot 自定义线程池

    本教程目录: 自定义线程池 配置spring默认的线程池 1. 自定义线程池 1.1 修改application.properties task.pool.corePoolSize=20 task.p ...

  8. Thread(线程)和ThreadPool(线程池) Thread回调与返回值

    Thread(线程) Thread开启线程:接收一个参数 TestClass tc = new TestClass(); //没有返回值,有一个object类型的参数的委托:两种写法. Paramet ...

  9. spring @Async 线程池使用

    最近公司项目正逐渐从dubbo向springCloud转型,在本次新开发的需求中,全部使用springcloud进行,在使用时线程池,考虑使用spring封装的线程池,现将本次使用心得及内容记录下来 ...

随机推荐

  1. [JQuery] JQuery学习笔记

    1.2019年10月20日14:43:48 学习HOW2J. 2.JQuery是一个javascript的框架,是对javascript的一种封装, 通过JQuery可以非常方便的操作html的元素 ...

  2. (C#)WPF:Grid面板介绍

    Grid:网格面板 Grid和其他各个Panel比较起来,功能最多也最为复杂.要使用Grid,首先要向RowDefinitions和ColumnDefinitions属性中添加一定数量的RowDefi ...

  3. secureCRT连接虚拟机

    1.secureCRT英文版下载 链接:https://pan.baidu.com/s/1LFWD-k2r4ZB7DHQA66QogQ 密码:khmo 破解方式参考 2.虚拟机静态IP设置 参考 3. ...

  4. 在input输入值改变reducer里的值

    输入值改变reducer里的值 通过store.dispatch传入reducer中,函数的第二个参数可以接收到 在reducer中 在todolist文件中 然后在把this.state中的值改变

  5. Python3 之 with语句(高效、便捷)

    在实际的编码过程中,有时有一些任务,需要事先做一些设置,事后做一些清理,这时就需要python3 with出场了,with能够对这样的需求进行一个比较优雅的处理,最常用的例子就是对访问文件的处理. 文 ...

  6. 2019-11-6:ubuntu安装配置JAVA环境

    1,下载JAVA,官方java 18下载网站:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-21331 ...

  7. (三)OpenStack---M版---双节点搭建---Keystone安装和配置

    ↓↓↓↓↓↓↓↓视频已上线B站↓↓↓↓↓↓↓↓ >>>>>>传送门 1.创建keystone数据库 2.创建随机密码作为管理员令牌 3.安装openstack-ke ...

  8. 从Excel到Python:最常用的36个Pandas函数

    本文涉及pandas最常用的36个函数,通过这些函数介绍如何完成数据生成和导入.数据清洗.预处理,以及最常见的数据分类,数据筛选,分类汇总,透视等最常见的操作. 生成数据表 常见的生成数据表的方法有两 ...

  9. 个人收藏-未整理--wince

    Wince 6.0 教程---第一课 环境搭建 分类: WINCE 2009-09-10 08:47 7622人阅读 评论(5) 收藏 举报 wincemicrosoftnetworkservicew ...

  10. vscode vue模版

    { "Print to console": { "prefix": "vue", "body": [ "< ...