concurrent.interpreters --- چندین مفسر در یک فرایند واحد¶
اضافه شده در نسخهی 3.14.
کد منبع: Lib/concurrent/interpreters
ماژول concurrent.interpreters رابطهای سطح بالاتر را بر فراز ماژول سطح پایینتر _interpreters ایجاد میکند.
این ماژول عمدتاً برای ارائه یک API پایه برای مدیریت مفسرها (معروف به «subinterpreters») و اجرای موارد در آنها در نظر گرفته شده است. اجرا عمدتاً شامل انتقال به یک مفسر (در نخ جاری) و فراخوانی یک تابع در آن زمینه اجرا است.
برای همروندی، خود مفسرها (و این ماژول) چیز چندانی بیشتر از جداسازی فراهم نمیکنند، که بهتنهایی مفید نیست. همروندی واقعی بهصورت جداگانه از طریق نخها در دسترس است -- پایین را ببینید.
همچنین ملاحظه نمائید
InterpreterPoolExecutorنخها را با مفسرها در یک رابط آشنا ترکیب میکند.
- جداسازی ماژولهای توسعه
چگونگی بهروزرسانی یک ماژول توسعه برای پشتیبانی از چندین مفسر.
دسترسپذیری: not WASI.
این ماژول روی WebAssembly کار نمیکند یا در دسترس نیست. برای اطلاعات بیشتر سکوهای WebAssembly را ببینید.
جزئیات کلیدی¶
پیش از آنکه بیشتر وارد بحث شویم، تعداد اندکی جزئیات وجود دارد که باید دربارهی استفاده از چندین مفسر در نظر داشته باشید:
ایزوله، بهطور پیشفرض
بدون نخهای ضمنی
هنوز همهی بستههای PyPI از استفاده در چندین مفسر پشتیبانی نمیکنند
مقدمه¶
یک «مفسر» عملاً زمینه اجرای رانتایم پایتون است. این شامل تمام وضعیت مورد نیاز رانتایم برای اجرای یک برنامه است. این شامل مواردی مانند وضعیت ایمپورت و توکارها میشود. (هر نخ، حتی اگر فقط نخ اصلی وجود داشته باشد، علاوه بر مفسر فعلی، دارای وضعیت اضافی رانتایم مربوط به استثنای فعلی و حلقه ارزیابی بایتکد است.)
مفهوم و کارکرد مفسر از نسخهی 2.2 بخشی از پایتون بوده است، اما این قابلیت تنها از طریق C-API در دسترس بود و چندان شناختهشده نبود، و جداسازی تا نسخهی 3.12 نسبتاً ناقص بود.
مفسرهای متعدد و جداسازی¶
یک پیادهسازی پایتون ممکن است از استفاده از چندین مفسر در یک فرایند پشتیبانی کند. CPython این پشتیبانی را دارد. هر مفسر عملاً از سایر مفسرها ایزوله است (با تعداد محدودی استثنا بر این قاعده، که سراسری در سطح فرایند هستند و بهدقت مدیریت میشوند).
آن جداسازی عمدتاً بهعنوان یک تفکیک قوی میان کامپوننتهای منطقی مجزای یک برنامه مفید است، جایی که میخواهید کنترل دقیقی بر نحوه تعامل آن کامپوننتها داشته باشید.
توجه
مفسرهای موجود در یک فرایند یکسان، از نظر فنی هرگز نمیتوانند بهطور کامل از یکدیگر جدا شوند، زیرا محدودیتهای اندکی برای دسترسی به حافظه در همان فرایند وجود دارد. رانتایم پایتون نهایت تلاش خود را برای جداسازی به کار میگیرد، اما ماژولهای توسعه ممکن است بهراحتی آن را نقض کنند. بنابراین، در موقعیتهای حساس امنیتی، جایی که نباید به دادههای یکدیگر دسترسی داشته باشند، از چندین مفسر استفاده نکنید.
اجرا در مفسر¶
اجرا در یک مفسر دیگر، مستلزم تعویض به آن در نخ جاری و سپس فراخوانی یک تابع است. رانتایم تابع را با استفاده از وضعیت مفسر جاری اجرا میکند. ماژول concurrent.interpreters یک API پایه برای ایجاد و مدیریت مفسرها و همچنین عملیات تعویض و فراخوانی (switch-and-call) ارائه میکند.
هیچ نخ دیگری بهطور خودکار برای این عملیات آغاز نمیشود. با این حال، برای این کار یک تابع کمکی وجود دارد. یک تابع کمکی اختصاصی دیگر برای فراخوانی exec() توکار در یک مفسر وجود دارد.
هنگامی که exec() (یا eval()) در یک مفسر فراخوانی میشوند، با استفاده از ماژول __main__ مفسر بهعنوان فضای نام «globals» اجرا میشوند. همین موضوع برای توابعی که با هیچ ماژولی مرتبط نیستند نیز صادق است. این مشابه روشی است که اسکریپتهای فراخوانیشده از خط فرمان در ماژول __main__ اجرا میشوند.
همروندی و موازیسازی¶
همانطور که پیشتر گفته شد، مفسرها بهخودی خود هیچگونه همروندیای فراهم نمیکنند. آنها صرفاً نمایانگر زمینه اجرای جداشدهای هستند که رانتایم در نخ جاری از آن استفاده خواهد کرد. این جداسازی آنها را شبیه به فرآیندها میکند، اما همچنان مانند نخها از کارایی درونفرآیندی برخوردارند.
با همه این اوصاف، مفسرها بهطور طبیعی از برخی سبکهای همروندی پشتیبانی میکنند. آن جداسازی اثر جانبی قدرتمندی دارد. این امر رویکرد متفاوتی به همروندی را ممکن میسازد، نسبت به آنچه میتوانید با ناهمگام یا نخها در پیش بگیرید. این مدل همروندی مشابه CSP یا مدل کنشگر (actor model) است؛ مدلی که استدلال درباره آن نسبتاً آسان است.
شما میتوانید از آن مدل همروندی در یک نخ واحد بهره ببرید و به سبک Stackless، بهصورت رفتوبرگشتی بین مفسرها جابهجا شوید. با این حال، این مدل زمانی مفیدتر است که مفسرها را با چندین نخ ترکیب کنید. این کار عمدتاً شامل شروع یک نخ جدید است، که در آن به مفسر دیگری جابهجا میشوید و آنچه را میخواهید در آنجا اجرا میکنید.
هر نخ واقعی در پایتون، حتی اگر فقط در نخ اصلی در حال اجرا باشید، زمینهی اجرای فعلی خود را دارد. چندین نخ میتوانند از یک مفسر مشترک یا مفسرهای متفاوت استفاده کنند.
در سطح بالا، میتوانید ترکیب نخها و مفسرها را بهعنوان نخهایی با اشتراکگذاری انتخابی در نظر بگیرید.
بهعنوان یک مزیت قابلتوجه، مفسرها بهاندازهای از یکدیگر ایزوله هستند که GIL را به اشتراک نمیگذارند؛ این بدان معناست که ترکیب نخها با چندین مفسر، موازیسازی کامل چندهستهای را ممکن میسازد. (این حالت از پایتون 3.12 برقرار بوده است.)
ارتباط میان مفسرها¶
در عمل، چندین مفسر تنها زمانی مفید هستند که راهی برای برقراری ارتباط بین آنها داشته باشیم. این موضوع معمولاً شامل نوعی پیامرسانی میشود، اما حتی میتواند به معنای اشتراکگذاری دادهها به شیوهای با مدیریت دقیق باشد.
با در نظر گرفتن این موضوع، ماژول concurrent.interpreters یک پیادهسازی از queue.Queue را ارائه میدهد که از طریق create_queue() در دسترس است.
مرجع¶
این ماژول توابع زیر را تعریف میکند:
- concurrent.interpreters.list_all()¶
یک
listاز اشیاءInterpreterبرمیگرداند، یکی برای هر مفسر موجود.
- concurrent.interpreters.get_current()¶
یک شیء
Interpreterبرای مفسر در حال اجرا برمیگرداند.
- concurrent.interpreters.get_main()¶
یک شیء
Interpreterبرای مفسر اصلی بازمیگرداند. این همان مفسری است که رانتایم برای اجرای REPL یا اسکریپت دادهشده در خط فرمان ایجاد کرده است. معمولاً تنها مفسر همین است.
- concurrent.interpreters.create()¶
یک مفسر پایتون جدید (بیکار) را راهاندازی میکند و یک شیء
Interpreterبرای آن برمیگرداند.
- concurrent.interpreters.create_queue()¶
یک صف بینمفسری جدید را مقداردهی اولیه میکند و یک شیء
Queueبرای آن برمیگرداند.
اشیای مفسر¶
- class concurrent.interpreters.Interpreter(id)¶
یک مفسر واحد در فرایند جاری.
بهطور کلی،
Interpreterنباید مستقیماً فراخوانی شود. در عوض، ازcreate()یا یکی دیگر از توابع ماژول استفاده کنید.- id¶
(فقطخواندنی)
شناسهی مفسر زیربنایی.
- whence¶
(فقطخواندنی)
رشتهای که منشأ مفسر را توصیف میکند.
- is_running()¶
اگر مفسر هماکنون در حال اجرای کد در ماژول
__main__خود باشد،Trueو در غیر این صورتFalseبرمیگرداند.
- close()¶
نهاییسازی و نابود کردن مفسر.
- prepare_main(ns=None, **kwargs)¶
اشیاء را در ماژول
__main__مفسر مقید کنید.برخی از شیءها در واقع به اشتراک گذاشته میشوند و برخی بهصورت کارآمد کپی میشوند، اما بیشتر آنها از طریق
pickleکپی میشوند. «اشتراکگذاری» اشیاء را ببینید.
- exec(code, /, dedent=True)¶
کد منبع دادهشده را در مفسر (در نخ فعلی) اجرا کنید.
- call(callable, /, *args, **kwargs)¶
Return the result of running the given function in the interpreter (in the current thread).
- call_in_thread(callable, /, *args, **kwargs)¶
تابع دادهشده را در مفسر اجرا کنید (در یک نخ جدید).
استثناها¶
- exception concurrent.interpreters.InterpreterError¶
این استثنا، زیرکلاسی از
Exception، زمانی پرتاب میشود که خطای مرتبط با مفسر رخ دهد.
- exception concurrent.interpreters.InterpreterNotFoundError¶
این استثنا، زیرکلاسی از
InterpreterError، زمانی پرتاب میشود که مفسر هدف دیگر وجود ندارد.
- exception concurrent.interpreters.ExecutionFailed¶
این استثنا، که زیرکلاسی از
InterpreterErrorاست، زمانی پرتاب میشود که کد در حال اجرا، استثنای گرفتهنشدهای را پرتاب کرده باشد.- excinfo¶
یک تصویر لحظهای ساده از استثنای پرتابشده در مفسر دیگر.
این استثنا، که زیرکلاسی از
TypeErrorاست، زمانی پرتاب میشود که نتوان یک شیء را به مفسر دیگری ارسال کرد.
ارتباط بین مفسرها¶
- class concurrent.interpreters.Queue(id)¶
پوششی برای یک صف بینمفسری سطح پایین، که رابط
queue.Queueرا پیادهسازی میکند. صف زیرین فقط از طریقcreate_queue()قابل ایجاد است.برخی از شیءها در واقع به اشتراک گذاشته میشوند و برخی بهصورت کارآمد کپی میشوند، اما بیشتر آنها از طریق
pickleکپی میشوند. «اشتراکگذاری» اشیاء را ببینید.- id¶
(فقطخواندنی)
شناسهی صف.
- exception concurrent.interpreters.QueueEmptyError¶
این استثنا، زیرکلاسی از
queue.Empty، زمانی ازQueue.get()وQueue.get_nowait()پرتاب میشود که صف خالی باشد.
- exception concurrent.interpreters.QueueFullError¶
این استثنا، زیرکلاسی از
queue.Full، هنگامی ازQueue.put()وQueue.put_nowait()پرتاب میشود که صف پر باشد.
استفادهی پایه¶
ایجاد یک مفسر و اجرای کد در آن:
from concurrent import interpreters
interp = interpreters.create()
# Run in the current OS thread.
interp.exec('print("spam!")')
interp.exec("""if True:
print('spam!')
""")
from textwrap import dedent
interp.exec(dedent("""
print('spam!')
"""))
def run(arg):
return arg
res = interp.call(run, 'spam!')
print(res)
def run():
print('spam!')
interp.call(run)
# Run in new OS thread.
t = interp.call_in_thread(run)
t.join()