این ماژول عمدتاً برای ارائه یک API پایه برای مدیریت مفسرها (معروف به «subinterpreters») و اجرای موارد در آنها در نظر گرفته شده است. اجرا عمدتاً شامل انتقال به یک مفسر (در نخ جاری) و فراخوانی یک تابع در آن زمینه اجرا است.
برای همروندی، خود مفسرها (و این ماژول) چیز چندانی بیشتر از جداسازی فراهم نمیکنند، که بهتنهایی مفید نیست. همروندی واقعی بهصورت جداگانه از طریق نخها در دسترس است -- پایین را ببینید.
مقدمه¶
یک «مفسر» عملاً زمینه اجرای رانتایم پایتون است. این شامل تمام وضعیت مورد نیاز رانتایم برای اجرای یک برنامه است. این شامل مواردی مانند وضعیت ایمپورت و توکارها میشود. (هر نخ، حتی اگر فقط نخ اصلی وجود داشته باشد، علاوه بر مفسر فعلی، دارای وضعیت اضافی رانتایم مربوط به استثنای فعلی و حلقه ارزیابی بایتکد است.)
مفهوم و کارکرد مفسر از نسخهی 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() در دسترس است.
«اشتراکگذاری» اشیاء¶
هر دادهای که واقعاً بین مفسرها به اشتراک گذاشته میشود، ایمنی نخی فراهمشده از سوی GIL را از دست میدهد. گزینههای مختلفی برای مدیریت این موضوع در ماژولهای توسعه وجود دارد. با این حال، از دید کد پایتون، فقدان ایمنی نخی به این معناست که اشیاء، بهجز چند استثنا، عملاً نمیتوانند به اشتراک گذاشته شوند. در عوض، باید یک رونوشت ایجاد شود، که به این معناست که اشیاء تغییرپذیر همگام باقی نخواهند ماند.
بهطور پیشفرض، بیشتر اشیاء هنگامی که به مفسر دیگری منتقل میشوند، با pickle کپی میشوند. تقریباً همهی اشیاء تغییرناپذیر توکار یا مستقیماً به اشتراک گذاشته میشوند یا بهصورت کارآمد کپی میشوند. برای مثال:
تعداد کمی از نوعهای پایتون وجود دارد که واقعاً دادههای تغییرپذیر را بین مفسرها به اشتراک میگذارند: