concurrent.futures --- راه‌اندازی وظایف موازی

اضافه شده در نسخه‌ی 3.2.

کد منبع: Lib/concurrent/futures/thread.py، Lib/concurrent/futures/process.py و Lib/concurrent/futures/interpreter.py


ماژول concurrent.futures یک رابط سطح بالا برای اجرای ناهمگام فراخوانی‌پذیرها فراهم می‌کند.

اجرای ناهمگام را می‌توان با نخ‌ها، با استفاده از ThreadPoolExecutor یا InterpreterPoolExecutor، یا با فرآیندهای جداگانه، با استفاده از ProcessPoolExecutor انجام داد. هر یک از آن‌ها رابط یکسانی را پیاده‌سازی می‌کند که توسط کلاس انتزاعی Executor تعریف شده است.

concurrent.futures.Future نباید با asyncio.Future اشتباه گرفته شود، که برای استفاده با وظایف و هم‌روال‌های asyncio طراحی شده است. برای مقایسه‌ی دقیق این دو، مستندات Future در asyncio را ببینید.

دسترس‌پذیری: not WASI.

این ماژول روی WebAssembly کار نمی‌کند یا در دسترس نیست. برای اطلاعات بیشتر سکوهای WebAssembly را ببینید.

اشیای Executor

class concurrent.futures.Executor

یک کلاس انتزاعی که متدهایی برای اجرای فراخوانی‌ها به‌صورت ناهمگام فراهم می‌کند. این کلاس نباید مستقیماً استفاده شود، بلکه باید از طریق زیرکلاس‌های عینی آن استفاده شود.

submit(fn, /, *args, **kwargs)

شیء فراخوانی‌پذیر fn را زمان‌بندی می‌کند تا به‌صورت fn(*args, **kwargs) اجرا شود و یک شیء Future را برمی‌گرداند که نشان‌دهنده‌ی اجرای آن شیء فراخوانی‌پذیر است.

with ThreadPoolExecutor(max_workers=1) as executor:
    future = executor.submit(pow, 323, 1235)
    print(future.result())
map(fn, *iterables, timeout=None, chunksize=1, buffersize=None)

مشابه map(fn, *iterables) با این تفاوت که:

  • iterables به‌جای جمع‌آوری به‌صورت تنبل، بلافاصله جمع‌آوری می‌شوند، مگر اینکه buffersize برای محدود کردن تعداد وظایف ارسال‌شده‌ای که نتایجشان هنوز تولید نشده‌اند تعیین شده باشد. اگر بافر پر باشد، پیمایش بر iterables تا زمانی که نتیجه‌ای از بافر تولید شود متوقف می‌شود.

  • fn به‌صورت ناهمگام اجرا می‌شود و ممکن است چندین فراخوانی fn به‌صورت همزمان انجام شود.

اگر __next__() فراخوانی شود و نتیجه پس از گذشت timeout ثانیه از فراخوانی اصلی Executor.map() در دسترس نباشد، پیمایش‌گر برگردانده‌شده استثنای TimeoutError را پرتاب می‌کند. timeout می‌تواند یک عدد صحیح یا عدد اعشاری باشد. اگر timeout مشخص نشده باشد یا None باشد، محدودیتی برای زمان انتظار وجود ندارد.

اگر یک فراخوانی fn استثنایی را پرتاب کند، آن استثنا زمانی پرتاب خواهد شد که مقدارش از پیمایش‌گر بازیابی شود.

هنگام استفاده از ProcessPoolExecutor، این متد پیمایش‌پذیرها را به تعدادی تکه تقسیم می‌کند و آن‌ها را به‌عنوان وظایف جداگانه به استخر ارسال می‌کند. اندازه‌ی (تقریبی) این تکه‌ها را می‌توان با تنظیم chunksize روی یک عدد صحیح مثبت مشخص کرد. برای پیمایش‌پذیرهای بسیار طولانی، استفاده از یک مقدار بزرگ برای chunksize می‌تواند در مقایسه با اندازه‌ی پیش‌فرض ۱، عملکرد را به‌طور قابل‌توجهی بهبود بخشد. در ThreadPoolExecutor و InterpreterPoolExecutor، chunksize هیچ تأثیری ندارد.

تغییر یافته در نسخه‌ی 3.5: پارامتر chunksize افزوده شد.

تغییر یافته در نسخه‌ی 3.14: پارامتر buffersize افزوده شد.

shutdown(wait=True, *, cancel_futures=False)

به اجراکننده (executor) اطلاع می‌دهد که باید هر منبعی را که در حال استفاده از آن است، هنگامی که اجرای آینده‌نماهای در انتظار فعلی (futures) به پایان رسید، آزاد کند. فراخوانی‌های Executor.submit() و Executor.map() که پس از shutdown انجام شوند، RuntimeError را پرتاب خواهند کرد.

اگر wait برابر True باشد، این متد تا زمانی که اجرای تمام آینده‌نماهای در انتظار (futures) به پایان نرسیده باشد و منابع مرتبط با اجراکننده (executor) آزاد نشده باشند، بازگشت نخواهد کرد. اگر wait برابر False باشد، این متد بلافاصله بازمی‌گردد و منابع مرتبط با اجراکننده زمانی آزاد می‌شوند که اجرای تمام آینده‌نماهای در انتظار به پایان برسد. صرف‌نظر از مقدار wait، کل برنامه پایتون تا زمانی که اجرای تمام آینده‌نماهای در انتظار به پایان نرسیده باشد، خارج نخواهد شد.

اگر cancel_futures برابر True باشد، این متد تمام آینده‌نماهای در انتظار را که اجراکننده اجرای آن‌ها را آغاز نکرده است، لغو می‌کند. آینده‌نماهایی که تکمیل شده‌اند یا در حال اجرا هستند، بدون توجه به مقدار cancel_futures لغو نخواهند شد.

اگر هر دو cancel_futures و wait برابر True باشند، تمام آینده‌نماهایی که اجراکننده اجرای آن‌ها را آغاز کرده است، پیش از بازگشت این متد تکمیل خواهند شد. آینده‌نماهای باقی‌مانده لغو می‌شوند.

اگر از اجراکننده به‌عنوان یک context manager از طریق دستور with استفاده کنید، نیازی به فراخوانی صریح این متد نخواهید داشت؛ در این حالت Executor خاموش می‌شود (منتظر می‌ماند، گویی Executor.shutdown() با wait روی True فراخوانی شده است):

import shutil
with ThreadPoolExecutor(max_workers=4) as e:
    e.submit(shutil.copy, 'src1.txt', 'dest1.txt')
    e.submit(shutil.copy, 'src2.txt', 'dest2.txt')
    e.submit(shutil.copy, 'src3.txt', 'dest3.txt')
    e.submit(shutil.copy, 'src4.txt', 'dest4.txt')

تغییر یافته در نسخه‌ی 3.9: cancel_futures اضافه شد.

ThreadPoolExecutor

ThreadPoolExecutor یک زیرکلاس از Executor است که از استخری از نخ‌ها برای اجرای فراخوانی‌ها به‌صورت ناهمگام استفاده می‌کند.

بن‌بست‌ها ممکن است زمانی رخ دهند که فراخوانی‌پذیر مرتبط با یک Future منتظر نتایج یک Future دیگر بماند. برای مثال:

import time
def wait_on_b():
    time.sleep(5)
    print(b.result())  # b will never complete because it is waiting on a.
    return 5

def wait_on_a():
    time.sleep(5)
    print(a.result())  # a will never complete because it is waiting on b.
    return 6


executor = ThreadPoolExecutor(max_workers=2)
a = executor.submit(wait_on_b)
b = executor.submit(wait_on_a)

و:

def wait_on_future():
    f = executor.submit(pow, 5, 2)
    # This will never complete because there is only one worker thread and
    # it is executing this function.
    print(f.result())

executor = ThreadPoolExecutor(max_workers=1)
future = executor.submit(wait_on_future)
# Note: calling future.result() would also cause a deadlock because
# the single worker thread is already waiting for wait_on_future().
class concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=())

یک زیرکلاس Executor که از استخری با حداکثر max_workers نخ برای اجرای فراخوانی‌ها به‌صورت ناهمگام استفاده می‌کند.

پیش از آنکه مفسر بتواند خارج شود، همه نخ‌هایی که در ThreadPoolExecutor در صف قرار گرفته‌اند، join خواهند شد. توجه داشته باشید که هندلر خروجی که این کار را انجام می‌دهد، پیش از هر هندلر خروجی که با استفاده از atexit اضافه شده باشد، اجرا می‌شود. این بدان معناست که برای علامت‌دهی به نخ‌ها جهت خروج به‌صورت ایمن، باید استثناهای نخ اصلی گرفته و مدیریت شوند. به همین دلیل، توصیه می‌شود که ThreadPoolExecutor برای وظایف طولانی‌مدت استفاده نشود.

initializer یک شیء فراخوانی‌پذیر اختیاری است که در آغاز هر نخ‌کارگر فراخوانی می‌شود؛ initargs تاپلی از آرگومان‌ها است که به initializer ارسال می‌شوند. در صورتی که initializer استثنایی پرتاب کند، همه کارهایی که در حال حاضر در انتظار هستند، و همچنین هر تلاشی برای ارسال کارهای بیشتر به استخر، یک BrokenThreadPool پرتاب خواهند کرد.

تغییر یافته در نسخه‌ی 3.5: اگر max_workers None باشد یا ارائه نشود، به‌طور پیش‌فرض برابر با تعداد پردازنده‌های ماشین ضرب در 5 خواهد بود، با این فرض که ThreadPoolExecutor اغلب برای هم‌پوشانی ورودی/خروجی به‌جای کار پردازنده استفاده می‌شود و تعداد کارگرها باید بیشتر از تعداد کارگرهای ProcessPoolExecutor باشد.

تغییر یافته در نسخه‌ی 3.6: پارامتر thread_name_prefix افزوده شد تا کاربران بتوانند نام‌های threading.Thread را برای نخ‌های کارگر ایجادشده توسط استخر، برای اشکال‌زدایی آسان‌تر کنترل کنند.

تغییر یافته در نسخه‌ی 3.7: آرگومان‌های initializer و initargs اضافه شدند.

تغییر یافته در نسخه‌ی 3.8: مقدار پیش‌فرض max_workers به min(32, os.cpu_count() + 4) تغییر کرده است. این مقدار پیش‌فرض حداقل ۵ کارگر را برای وظایف محدود به I/O حفظ می‌کند. این مقدار حداکثر از ۳۲ هسته‌ی CPU برای وظایف محدود به CPU که GIL را آزاد می‌کنند، استفاده می‌کند. و از استفاده‌ی ضمنی از منابع بسیار زیاد در ماشین‌های با تعداد هسته بسیار زیاد جلوگیری می‌کند.

ThreadPoolExecutor اکنون پیش از راه‌اندازی max_workers نخ کاری، از نخ‌های کاری بیکار نیز دوباره استفاده می‌کند.

تغییر یافته در نسخه‌ی 3.13: مقدار پیش‌فرض max_workers به min(32, (os.process_cpu_count() or 1) + 4) تغییر کرده است.

مثال ThreadPoolExecutor

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://nonexistent-subdomain.python.org/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

InterpreterPoolExecutor

اضافه شده در نسخه‌ی 3.14.

کلاس InterpreterPoolExecutor از استخری از مفسرها استفاده می‌کند تا فراخوانی‌ها را به‌صورت ناهمگام اجرا کند. این کلاس یک زیرکلاس از ThreadPoolExecutor است، که به این معناست که هر کارگر در نخ خود اجرا می‌شود. تفاوت در اینجا این است که هر کارگر مفسر خود را دارد و هر وظیفه را با استفاده از آن مفسر اجرا می‌کند.

بزرگ‌ترین مزیت استفاده از مفسرها به‌جای استفاده صرف از نخ‌ها، موازی‌سازی چندهسته‌ای واقعی است. هر مفسر دارای قفل سراسری مفسر مختص خود است، بنابراین کد در حال اجرا در یک مفسر می‌تواند بر یک هسته‌ی CPU اجرا شود، در حالی که کد در مفسری دیگر بدون مسدود شدن بر هسته‌ی دیگری اجرا می‌شود.

بهای این کار آن است که نوشتن کد هم‌روند برای استفاده با چندین مفسر ممکن است تلاش بیشتری بطلبد. با این حال، دلیل این موضوع آن است که این کار شما را ملزم می‌کند درباره‌ی چگونگی و زمان تعامل مفسرها با یکدیگر سنجیده عمل کنید و به‌صراحت مشخص کنید چه داده‌هایی بین مفسرها مشترک است. این امر چندین مزیت به همراه دارد که به جبران این تلاش اضافی کمک می‌کنند، از جمله موازی‌سازی واقعی چند هسته‌ای. برای مثال، کدی که به این شیوه نوشته شده باشد می‌تواند استدلال درباره‌ی هم‌روندی را آسان‌تر کند. مزیت مهم دیگر این است که شما مجبور نیستید با چندین نقطه‌ی دردساز بزرگ استفاده از نخ‌ها، مانند شرایط رقابتی، سروکار داشته باشید.

مفسر هر کارگر از همه مفسرهای دیگر ایزوله است. «ایزوله» یعنی هر مفسر وضعیت ران‌تایم خودش را دارد و به‌طور کاملاً مستقل عمل می‌کند. برای مثال، اگر sys.stdout را در یک مفسر تغییر مسیر دهید، این تغییر مسیر به‌طور خودکار در هیچ مفسر دیگری اعمال نمی‌شود. اگر ماژولی را در یک مفسر ایمپورت کنید، به‌طور خودکار در هیچ مفسر دیگری ایمپورت نمی‌شود. لازم است آن ماژول را به‌طور جداگانه در مفسری که به آن نیاز دارید ایمپورت کنید. در واقع، هر ماژول ایمپورت‌شده در یک مفسر، یک شیء کاملاً جدا از همان ماژول در مفسری دیگر است، از جمله sys، builtins و حتی __main__.

جداسازی به این معناست که نمی‌توان همزمان از یک شیء تغییرپذیر، یا داده‌های دیگر، در بیش از یک مفسر استفاده کرد. این عملاً به این معناست که مفسرها در واقع نمی‌توانند چنین اشیاء یا داده‌هایی را به اشتراک بگذارند. در عوض، هر مفسر باید نسخه‌ی خود را داشته باشد و شما باید هرگونه تغییر بین نسخه‌ها را به‌صورت دستی همگام‌سازی کنید. اشیاء و داده‌های تغییرناپذیر، مانند تک‌نمونه‌های توکار، رشته‌ها و تاپل‌هایی از اشیاء تغییرناپذیر، این محدودیت‌ها را ندارند.

ارتباط و همگام‌سازی بین مفسرها به مؤثرترین سینتکس با استفاده از ابزارهای اختصاصی انجام می‌شود، مانند آن‌هایی که در PEP 734 پیشنهاد شده‌اند. یک جایگزین کم‌بازده‌تر، سریال‌سازی با pickle و سپس ارسال بایت‌ها از طریق یک socket یا pipe مشترک است.

class concurrent.futures.InterpreterPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=())

زیرکلاسی از ThreadPoolExecutor که فراخوانی‌ها را به‌صورت ناهمگام با استفاده از استخری از حداکثر max_workers نخ اجرا می‌کند. هر نخ وظایف را در مفسر خود اجرا می‌کند. مفسرهای کارگر از یکدیگر جدا هستند، که یعنی هر مفسر وضعیت ران‌تایم خود را دارد و نمی‌تواند هیچ شیء تغییرپذیر یا داده دیگری را به اشتراک بگذارد. هر مفسر دارای قفل سراسری مفسر خود است، که یعنی کدی که با این اجراکننده اجرا می‌شود، از موازی‌سازی چندهسته‌ای واقعی برخوردار است.

آرگومان‌های اختیاری initializer و initargs همان معنایی را دارند که برای ThreadPoolExecutor دارند: initializer هنگام ایجاد هر کارگر اجرا می‌شود، هرچند در این حالت در مفسر کارگر اجرا می‌شود. اجراکننده، initializer و initargs را هنگام ارسال آن‌ها به مفسر کارگر با استفاده از pickle سریال‌سازی می‌کند.

توجه

اجراکننده ممکن است استثناهای گرفته‌نشده از initializer را با ExecutionFailed جایگزین کند.

سایر نکات احتیاطی مربوط به کلاس والد ThreadPoolExecutor در اینجا نیز اعمال می‌شوند.

submit() و map() مانند حالت عادی کار می‌کنند، با این تفاوت که کارگر، شیء فراخوانی‌پذیر و آرگومان‌ها را هنگام ارسال آن‌ها به مفسر خود، با استفاده از pickle سریال‌سازی می‌کند. کارگر نیز به همین ترتیب مقدار بازگشتی را هنگام بازگرداندن آن سریال‌سازی می‌کند.

هنگامی که وظیفه فعلی یک کارگر یک استثنای گرفته‌نشده پرتاب می‌کند، کارگر همیشه تلاش می‌کند استثنا را به‌همان‌صورت حفظ کند. اگر این کار موفقیت‌آمیز باشد، __cause__ را نیز به یک نمونه متناظر از ExecutionFailed تنظیم می‌کند که شامل خلاصه‌ای از استثنای اصلی است. در حالت غیرمعمولی که کارگر نتواند استثنای اصلی را به‌همان‌صورت حفظ کند، در عوض نمونه متناظر از ExecutionFailed را مستقیماً حفظ می‌کند.

ProcessPoolExecutor

کلاس ProcessPoolExecutor یک زیرکلاس از Executor است که از استخری از فرایندها برای اجرای ناهمگام فراخوانی‌ها استفاده می‌کند. ProcessPoolExecutor از ماژول multiprocessing استفاده می‌کند؛ این ماژول امکان دور زدن قفل مفسر سراسری را فراهم می‌کند، اما همچنین به این معناست که فقط اشیای پیکل‌پذیر می‌توانند اجرا یا بازگردانده شوند.

ماژول __main__ باید توسط زیرفرایندهای کارگر قابل ایمپورت باشد. این بدان معناست که ProcessPoolExecutor در مفسر تعاملی کار نخواهد کرد.

فراخوانی متدهای Executor یا Future از یک شیء فراخوانی‌پذیر که به ProcessPoolExecutor ارسال‌شده است، منجر به بن‌بست می‌شود.

توجه داشته باشید که محدودیت‌های مربوط به پیکل‌پذیری بودن توابع و آرگومان‌ها، مطابق multiprocessing.Process، هنگام استفاده از submit() و map() بر روی ProcessPoolExecutor اعمال می‌شوند. نباید انتظار داشته باشید که تابعی که در یک REPL یا به‌صورت یک lambda تعریف شده است، کار کند.

class concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None, initializer=None, initargs=(), max_tasks_per_child=None)

یک کلاس فرعی از Executor که فراخوانی‌ها را به‌صورت ناهمگام با استفاده از استخری از حداکثر max_workers فرآیند اجرا می‌کند. اگر max_workers برابر None باشد یا داده نشود، مقدار پیش‌فرض آن os.process_cpu_count() خواهد بود. اگر max_workers کمتر یا مساوی با 0 باشد، یک ValueError پرتاب خواهد شد. در ویندوز، max_workers باید کمتر یا مساوی با 61 باشد. در غیر این صورت، ValueError پرتاب خواهد شد. اگر max_workers برابر None باشد، مقدار پیش‌فرض انتخاب‌شده حداکثر 61 خواهد بود، حتی اگر پردازنده‌های بیشتری در دسترس باشند. mp_context می‌تواند یک زمینه multiprocessing یا None باشد. از آن برای راه‌اندازی کارگرها استفاده خواهد شد. اگر mp_context برابر None باشد یا داده نشود، از زمینه پیش‌فرض multiprocessing استفاده می‌شود. زمینه‌ها و متدهای شروع را ببینید.

initializer یک فراخوانی‌پذیر اختیاری است که در آغاز هر فرایند کارگر فراخوانی می‌شود؛ initargs تاپلی از آرگومان‌های ارسال‌شده به initializer است. اگر initializer استثنایی پرتاب کند، تمام کارهای در انتظار فعلی و همچنین هر تلاشی برای ارسال کارهای بیشتر به استخر، BrokenProcessPool را پرتاب خواهند کرد.

max_tasks_per_child یک آرگومان اختیاری است که حداکثر تعداد وظایفی را مشخص می‌کند که یک فرایند واحد می‌تواند پیش از خروج و جایگزینی با یک فرایند کارگر تازه اجرا کند. به‌طور پیش‌فرض max_tasks_per_child برابر None است، به این معنا که فرایندهای کارگر تا زمانی که استخر وجود دارد زنده می‌مانند. هرگاه یک مقدار حداکثری مشخص شود، در نبود پارامتر mp_context، روش شروع چندپردازشی "spawn" به‌طور پیش‌فرض استفاده می‌شود. این قابلیت با روش شروع "fork" ناسازگار است.

تغییر یافته در نسخه‌ی 3.3: هنگامی که یکی از فرایندهای کارگر به‌طور ناگهانی خاتمه یابد، اکنون یک خطای BrokenProcessPool پرتاب می‌شود. پیش‌تر، رفتار تعریف‌نشده بود، اما عملیات‌ها روی اجراکننده (executor) یا آینده‌نماهای (futures) آن اغلب قفل می‌شدند یا به بن‌بست می‌رسیدند.

تغییر یافته در نسخه‌ی 3.7: آرگومان mp_context افزوده شد تا کاربران بتوانند start_method را برای فرایندهای کارگر ایجادشده توسط استخر کنترل کنند.

آرگومان‌های initializer و initargs اضافه شدند.

تغییر یافته در نسخه‌ی 3.11: آرگومان max_tasks_per_child افزوده شد تا کاربران بتوانند طول عمر کارگران استخر را کنترل کنند.

تغییر یافته در نسخه‌ی 3.12: در سیستم‌های POSIX، اگر برنامه شما چندین نخ دارد و زمینه‌ی multiprocessing از روش شروع "fork" استفاده می‌کند: تابع os.fork() که به‌صورت داخلی برای ایجاد کارگرها فراخوانی می‌شود، ممکن است یک DeprecationWarning پرتاب کند. یک mp_context را که برای استفاده از یک روش شروع متفاوت پیکربندی شده است، ارسال کنید. برای توضیح بیشتر، مستندات os.fork() را ببینید.

تغییر یافته در نسخه‌ی 3.13: max_workers به‌طور پیش‌فرض از os.process_cpu_count() استفاده می‌کند، نه os.cpu_count().

تغییر یافته در نسخه‌ی 3.14: متد پیش‌فرض شروع فرایند (به زمینه‌ها و متدهای شروع مراجعه کنید) از fork تغییر کرده است. اگر به متد شروع fork برای ProcessPoolExecutor نیاز دارید، باید به‌صراحت mp_context=multiprocessing.get_context("fork") را ارسال کنید.

تغییر یافته در نسخه‌ی 3.14.7: بن‌بستی (gh-115634) برطرف شد که در آن اجراکننده ممکن بود پس از خروج یک فرایند کارگر در اثر رسیدن به حد max_tasks_per_child، در حالی که وظایفی در صف باقی مانده بودند، معلق بماند.

terminate_workers()

تلاش می‌کند تا با فراخوانی Process.terminate روی هر یک از آن‌ها، بلافاصله تمام فرایندهای کارگر زنده را خاتمه دهد. در داخل، Executor.shutdown() را نیز فراخوانی می‌کند تا اطمینان حاصل شود که تمام منابع دیگر مرتبط با اجراکننده آزاد شده‌اند.

پس از فراخوانی این متد، فراخواننده دیگر نباید وظایفی را به اجراکننده (executor) ارسال کند.

اضافه شده در نسخه‌ی 3.14.

kill_workers()

تلاش می‌کند تمام فرآیندهای کارگر زنده را بلافاصله با فراخوانی Process.kill برای هر یک از آن‌ها از بین ببرد. به‌صورت داخلی، همچنین Executor.shutdown() را نیز فراخوانی می‌کند تا اطمینان حاصل شود که همه منابع دیگر مرتبط با اجراکننده آزاد می‌شوند.

پس از فراخوانی این متد، فراخواننده دیگر نباید وظایفی را به اجراکننده (executor) ارسال کند.

اضافه شده در نسخه‌ی 3.14.

مثال ProcessPoolExecutor

import concurrent.futures
import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]

def is_prime(n):
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))

if __name__ == '__main__':
    main()

اشیای Future

کلاس Future اجرای ناهمگام یک شیء فراخوانی‌پذیر را در بر می‌گیرد. نمونه‌های Future توسط Executor.submit() ایجاد می‌شوند.

class concurrent.futures.Future

اجرای ناهمگام یک فراخوانی‌پذیر را کپسوله می‌کند. نمونه‌های Future توسط Executor.submit() ایجاد می‌شوند و نباید به‌صورت مستقیم ایجاد شوند، مگر برای آزمون.

cancel()

برای لغو فراخوانی تلاش می‌کند. اگر فراخوانی در حال اجرا باشد یا اجرای آن به پایان رسیده باشد و نتوان آن را لغو کرد، متد False را برمی‌گرداند، در غیر این صورت فراخوانی لغو می‌شود و متد True را برمی‌گرداند.

cancelled()

اگر فراخوانی با موفقیت لغو شده باشد، True برگردانده می‌شود.

running()

اگر فراخوانی در حال حاضر در حال اجرا باشد و قابل لغو نباشد، True را برمی‌گرداند.

done()

اگر فراخوانی با موفقیت لغو شد یا اجرای آن به پایان رسید، True را برمی‌گرداند.

result(timeout=None)

مقدار برگردانده‌شده از فراخوانی را برمی‌گرداند. اگر فراخوانی هنوز به پایان نرسیده باشد، این متد حداکثر به مدت timeout ثانیه منتظر می‌ماند. اگر فراخوانی ظرف timeout ثانیه به پایان نرسد، استثنای TimeoutError پرتاب می‌شود. timeout می‌تواند int یا float باشد. اگر timeout مشخص نشده باشد یا None باشد، محدودیتی برای زمان انتظار وجود ندارد.

اگر فیوچر (future) پیش از تکمیل لغو شود، CancelledError پرتاب می‌شود.

اگر فراخوانی استثنایی را پرتاب کرد، این متد همان استثنا را پرتاب خواهد کرد.

exception(timeout=None)

استثنای پرتاب‌شده توسط فراخوانی را بازمی‌گرداند. اگر فراخوانی هنوز کامل نشده باشد، این متد حداکثر تا timeout ثانیه صبر می‌کند. اگر فراخوانی ظرف timeout ثانیه کامل نشود، یک TimeoutError پرتاب خواهد شد. timeout می‌تواند int یا float باشد. اگر timeout مشخص نشده باشد یا None باشد، محدودیتی برای زمان انتظار وجود ندارد.

اگر فیوچر (future) پیش از تکمیل لغو شود، CancelledError پرتاب می‌شود.

اگر فراخوانی بدون پرتاب استثنا به پایان برسد، None برگردانده می‌شود.

add_done_callback(fn)

fn فراخوانی‌پذیر را به آینده متصل می‌کند. هنگامی که future لغو شود یا اجرای آن به پایان برسد، fn با future به‌عنوان تنها آرگومان خود فراخوانی می‌شود.

فراخوانی‌پذیرهای افزوده‌شده به ترتیبی که افزوده شده‌اند فراخوانی می‌شوند و همیشه در نخی متعلق به فرایندی که آن‌ها را افزوده است فراخوانی می‌شوند. اگر فراخوانی‌پذیر زیرکلاسی از Exception را پرتاب کند، ثبت و نادیده گرفته می‌شود. اگر فراخوانی‌پذیر زیرکلاسی از BaseException را پرتاب کند، رفتار تعریف‌نشده است.

اگر فیوچر از قبل تکمیل شده یا لغو شده باشد، fn بلافاصله فراخوانی خواهد شد.

متدهای زیر از Future برای استفاده در آزمون واحد‌ها و پیاده‌سازی‌های Executor در نظر گرفته شده‌اند.

set_running_or_notify_cancel()

این متد باید فقط توسط پیاده‌سازی‌های Executor پیش از اجرای کار مرتبط با Future و توسط آزمون واحد‌ها فراخوانی شود.

اگر متد False را برگرداند، آنگاه Future لغو شده است، یعنی Future.cancel() فراخوانی شده و True را برگردانده است. تمام نخ‌هایی که در انتظار تکمیل Future هستند (یعنی از طریق as_completed() یا wait()) بیدار خواهند شد.

اگر متد True را برگرداند، Future لغو نشده و در وضعیت در حال اجرا قرار گرفته است، یعنی فراخوانی‌های Future.running() مقدار True را برمی‌گردانند.

این متد فقط یک بار فراخوانی‌پذیر است و پس از فراخوانی Future.set_result() یا Future.set_exception() نمی‌تواند فراخوانی شود.

set_result(result)

نتیجه‌ی کار مرتبط با Future را برابر result قرار می‌دهد.

این متد باید فقط توسط پیاده‌سازی‌های Executor و آزمون واحد‌ها مورد استفاده قرار گیرد.

تغییر یافته در نسخه‌ی 3.8: این متد در صورتی که Future از قبل انجام شده باشد، concurrent.futures.InvalidStateError را پرتاب می‌کند.

set_exception(exception)

نتیجه‌ی کار مرتبط با Future را برابر با Exception exception قرار می‌دهد.

این متد باید فقط توسط پیاده‌سازی‌های Executor و آزمون واحد‌ها مورد استفاده قرار گیرد.

تغییر یافته در نسخه‌ی 3.8: این متد در صورتی که Future از قبل انجام شده باشد، concurrent.futures.InvalidStateError را پرتاب می‌کند.

توابع ماژول

concurrent.futures.wait(fs, timeout=None, return_when=ALL_COMPLETED)

منتظر بمانید تا نمونه‌های Future داده‌شده توسط fs (که ممکن است توسط نمونه‌های مختلف Executor ایجاد شده باشند) کامل شوند. آینده‌نماهای تکراری داده‌شده به fs حذف می‌شوند و تنها یک بار برگردانده خواهند شد. یک تاپل دوتایی نام‌دار از مجموعه‌ها برمی‌گرداند. مجموعه اول، که done نام دارد، شامل آینده‌نماهایی است که پیش از کامل شدن انتظار، کامل شده‌اند (آینده‌نماهای تمام‌شده یا لغوشده). مجموعه دوم، که not_done نام دارد، شامل آینده‌نماهایی است که کامل نشده‌اند (آینده‌نماهای در انتظار یا در حال اجرا).

می‌توان از timeout برای کنترل حداکثر تعداد ثانیه‌های انتظار پیش از بازگشت استفاده کرد. timeout می‌تواند یک int یا float باشد. اگر timeout مشخص نشده باشد یا None باشد، محدودیتی برای زمان انتظار وجود ندارد.

return_when مشخص می‌کند که این تابع چه زمانی باید بازگشت کند. این مقدار باید یکی از ثابت‌های زیر باشد:

ثابت

توضیحات

concurrent.futures.FIRST_COMPLETED

تابع زمانی برمی‌گردد که هر آینده به پایان برسد یا لغو شود.

concurrent.futures.FIRST_EXCEPTION

این تابع هنگامی بازگشت خواهد کرد که هر فیوچری با پرتاب استثنا به پایان برسد. اگر هیچ فیوچری استثنایی پرتاب نکند، معادل ALL_COMPLETED است.

concurrent.futures.ALL_COMPLETED

تابع زمانی بازمی‌گردد که همه‌ی آینده‌نماها (futures) به پایان برسند یا لغو شوند.

concurrent.futures.as_completed(fs, timeout=None)

پیمایش‌گری بر روی نمونه‌های Future داده‌شده توسط fs (که ممکن است توسط نمونه‌های مختلف Executor ایجادشده باشند) برمی‌گرداند که Futureها را به‌محض کامل‌شدن (Futureهای تمام‌شده یا لغوشده) تولید می‌کند. هر Future داده‌شده توسط fs که تکراری باشد، تنها یک بار برگردانده می‌شود. هر Future که پیش از فراخوانی as_completed() کامل‌شده باشد، ابتدا تولید می‌شود. پیمایش‌گر برگردانده‌شده در صورتی یک TimeoutError پرتاب می‌کند که __next__() فراخوانی شود و نتیجه پس از timeout ثانیه از فراخوانی اصلی as_completed() در دسترس نباشد. timeout می‌تواند یک int یا float باشد. اگر timeout تعیین‌نشده باشد یا None باشد، محدودیتی برای زمان انتظار وجود ندارد.

همچنین ملاحظه نمائید

PEP 3148 -- آینده‌نماها (futures) - اجرای محاسبات به‌صورت ناهمگام

پیشنهادی که این قابلیت را برای گنجاندن در کتابخانه استاندارد پایتون توصیف می‌کرد.

کلاس‌های استثنا

exception concurrent.futures.CancelledError

هنگامی که یک آینده لغو می‌شود، پرتاب می‌شود.

exception concurrent.futures.TimeoutError

یک نام مستعار منسوخ‌شده از TimeoutError است که هنگامی که یک عملیات آتی از مهلت زمانی داده‌شده فراتر برود، پرتاب می‌شود.

تغییر یافته در نسخه‌ی 3.11: این کلاس به نام مستعاری برای TimeoutError تبدیل شد.

exception concurrent.futures.BrokenExecutor

این کلاس استثنا که از RuntimeError مشتق شده است، زمانی پرتاب می‌شود که یک اجراکننده (executor) به دلیلی خراب شده باشد و نتوان از آن برای ارسال یا اجرای وظایف جدید استفاده کرد.

اضافه شده در نسخه‌ی 3.7.

exception concurrent.futures.InvalidStateError

زمانی پرتاب می‌شود که یک عملیات غیرمجاز در وضعیت فعلی روی یک future انجام شود.

اضافه شده در نسخه‌ی 3.8.

exception concurrent.futures.thread.BrokenThreadPool

این کلاس استثنا که از BrokenExecutor مشتق شده است، زمانی پرتاب می‌شود که یکی از کارگرهای ThreadPoolExecutor در راه‌اندازی ناموفق باشد.

اضافه شده در نسخه‌ی 3.7.

exception concurrent.futures.interpreter.BrokenInterpreterPool

این کلاس استثنا، مشتق از BrokenThreadPool، زمانی پرتاب می‌شود که یکی از کارگرهای InterpreterPoolExecutor در راه‌اندازی ناموفق بوده باشد.

اضافه شده در نسخه‌ی 3.14.

exception concurrent.futures.process.BrokenProcessPool

این کلاس استثنا از BrokenExecutor (پیش‌تر RuntimeError) مشتق شده است و زمانی پرتاب می‌شود که یکی از کارگرهای ProcessPoolExecutor به‌صورت غیرپاکیزه خاتمه یافته باشد (برای مثال، اگر از بیرون کشته شده باشد).

اضافه شده در نسخه‌ی 3.3.