42

一部の機能は、Web サーバー上で非同期に実行する必要があります。メールの送信やデータの後処理は、典型的な使用例です。

関数を非同期で実行するためのデコレータ関数を作成する最良の (または最も Pythonic な) 方法は何ですか?

私のセットアップは一般的なものです: Python、Django、Gunicorn または Waitress、AWS EC2 標準 Linux

たとえば、次のように開始します。

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

希望の使用法:

@postpone
def foo():
    pass #do stuff
4

4 に答える 4

69

この実装を大規模かつ本番環境で問題なく使用し続けています。

デコレータの定義:

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

使用例:

@start_new_thread
def foo():
  #do stuff

時間の経過とともに、スタックは確実に更新および移行されました。

元は Python 2.4.7、Django 1.4、Gunicorn 0.17.2 でしたが、現在は Python 3.6、Django 2.1、ウェイトレス 1.1 です。

データベース トランザクションを使用している場合、Django は新しい接続を作成するため、手動で閉じる必要があります。

from django.db import connection

@postpone
def foo():
  #do stuff
  connection.close()
于 2015-03-07T09:11:28.677 に答える
2

tomcounsell のアプローチは、着信ジョブが多すぎない場合にうまく機能します。多くの持続的なジョブが短期間に実行されると、多くのスレッドが生成され、メイン プロセスが影響を受けます。この場合、コルーチンでスレッド プールを使用できます。

# 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)
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
于 2019-01-30T02:12:18.863 に答える