Python Django的多线程

Multithreading for Python Django

一些函数应该在Web服务器上异步运行。发送电子邮件或数据后处理是典型的用例。

编写decorator函数异步运行函数的最佳(或大多数pythonic)方法是什么?

我的设置很常见:python、django、gunicorn或waitress、AWS EC2标准Linux

例如,下面是一个开始:

1
2
3
4
5
6
7
8
from threading import Thread

def postpone(function):
    def decorator(*args, **kwargs):
        t = Thread(target = function, args=args, kwargs=kwargs)
        t.daemon = True
        t.start()
    return decorator

预期用途:

1
2
3
@postpone
def foo():
    pass #do stuff


我继续在大规模生产中使用这个实现,没有任何问题。

装饰器定义:

1
2
3
4
5
6
def start_new_thread(function):
    def decorator(*args, **kwargs):
        t = Thread(target = function, args=args, kwargs=kwargs)
        t.daemon = True
        t.start()
    return decorator

示例用法:

1
2
3
@start_new_thread
def foo():
  #do stuff

随着时间的推移,堆栈已经更新和转换,没有失败。

原来是python 2.4.7,django 1.4,gunicorn 0.17.2,现在是python 3.6,django 2.1,waitress 1.1。

如果使用任何数据库事务,Django将创建一个新的连接,需要手动关闭:

1
2
3
4
5
6
from django.db import connection

@postpone
def foo():
  #do stuff
  connection.close()


芹菜是一个异步任务队列/作业队列。它有很好的文档记录,非常适合您的需要。我建议你从这里开始


在Django进行异步处理最常见的方法是使用芹菜和django-celery


如果没有太多的新工作,Tomadvisil的方法很有效。如果许多持久的作业在短时间内运行,从而产生大量线程,那么主进程将受到影响。在这种情况下,可以使用带有协程的线程池,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# in my_utils.py

from concurrent.futures import ThreadPoolExecutor

MAX_THREADS = 10


def run_thread_pool():
   """
    Note that this is not a normal function, but a coroutine.
    All jobs are enqueued first before executed and there can be
    no more than 10 threads that run at any time point.
   """

    with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
        while True:
            func, args, kwargs = yield
            executor.submit(func, *args, **kwargs)


pool_wrapper = run_thread_pool()

# Advance the coroutine to the first yield (priming)
next(pool_wrapper)

1
2
3
4
5
6
7
8
9
from my_utils import pool_wrapper

def job(*args, **kwargs):
    # do something

def handle(request):
    # make args and kwargs
    pool_wrapper.send((job, args, kwargs))
    # return a response