چندین مفسر در یک فرایند پایتون¶
هرچند در بیشتر موارد شما تنها یک مفسر پایتون را تعبیه میکنید، مواردی وجود دارد که نیاز دارید چندین مفسر مستقل را در همان فرایند و شاید حتی در همان نخ ایجاد کنید. زیرمفسرها به شما امکان انجام این کار را میدهند.
مفسر «اصلی» نخستین مفسری است که هنگام راهاندازی رانتایم ایجاد میشود. این مفسر معمولاً تنها مفسر پایتون در یک فرایند است. برخلاف زیرمفسرها، مفسر اصلی مسئولیتهای منحصربهفردی در سطح کل فرایند مانند مدیریت سیگنال را بر عهده دارد. این مفسر همچنین مسئول اجرا در طول راهاندازی رانتایم است و معمولاً مفسر فعال در طول نهاییسازی رانتایم است. تابع PyInterpreterState_Main() اشارهگری به وضعیت آن برمیگرداند.
میتوانید با استفاده از تابع PyThreadState_Swap() بین زیرمفسرها جابهجا شوید. میتوانید آنها را با استفاده از توابع زیر ایجاد و نابود کنید:
-
type PyInterpreterConfig¶
ساختاری که بیشتر پارامترهای لازم برای پیکربندی یک زیرمفسر را در بر دارد. مقادیر آن تنها در Py_NewInterpreterFromConfig() استفاده میشوند و هرگز توسط رانتایم تغییر داده نمیشوند.
اضافه شده در نسخهی 3.12.
فیلدهای ساختار:
-
int use_main_obmalloc¶
اگر این مقدار 0 باشد، زیرمفسر از وضعیت تخصیصدهندهی «object» خود استفاده میکند. در غیر این صورت از وضعیت تخصیصدهندهی مفسر اصلی (به اشتراک) استفاده میکند.
اگر این مقدار 0 باشد، آنگاه check_multi_interp_extensions باید 1 (ناصفر) باشد. اگر این مقدار 1 باشد، آنگاه gil نباید PyInterpreterConfig_OWN_GIL باشد.
-
int allow_fork¶
اگر این مقدار 0 باشد، رانتایم از فورک کردن فرایند در هیچ نخی که زیرمفسر در حال حاضر در آن فعال است پشتیبانی نخواهد کرد. در غیر این صورت، فورک بدون محدودیت است.
توجه داشته باشید که ماژول subprocess وقتی فورک ممنوع است، همچنان کار میکند.
-
int allow_exec¶
اگر این مقدار 0 باشد، رانتایم در هیچ نخی که زیرمفسر در حال حاضر در آن فعال است، از جایگزینی فرایند جاری از طریق exec (مثلاً os.execv()) پشتیبانی نخواهد کرد. در غیر این صورت، exec بدون محدودیت است.
توجه داشته باشید که ماژول subprocess هنگامی که exec ممنوع است، همچنان کار میکند.
-
int allow_threads¶
اگر این مقدار 0 باشد، ماژول threading زیرمفسر نخها را ایجاد نمیکند. در غیر این صورت، نخها مجاز هستند.
-
int allow_daemon_threads¶
اگر این مقدار 0 باشد، ماژول threading در زیرمفسر نخهای دِیمِن ایجاد نمیکند. در غیر این صورت، نخهای دِیمِن مجاز هستند (تا زمانی که allow_threads ناصفر باشد).
-
int check_multi_interp_extensions¶
اگر این مقدار 0 باشد، همه ماژولهای توسعهای، از جمله ماژولهای قدیمی (با مقداردهی اولیه تکمرحلهای)، میتوانند در هر نخی که زیرمفسر در حال حاضر در آن فعال است ایمپورت شوند. در غیر این صورت، فقط ماژولهای توسعهای با مقداردهی اولیه چندمرحلهای (نگاه کنید به PEP 489) میتوانند ایمپورت شوند. (همچنین Py_mod_multiple_interpreters را ببینید.)
اگر use_main_obmalloc برابر 0 باشد، این مقدار باید 1 (ناصفر) باشد.
-
int gil¶
این، عملکرد قفل مفسر سراسری را برای زیرمفسر تعیین میکند. این میتواند یکی از موارد زیر باشد:
-
PyInterpreterConfig_DEFAULT_GIL¶
از انتخاب پیشفرض (PyInterpreterConfig_SHARED_GIL) استفاده کنید.
-
PyInterpreterConfig_SHARED_GIL¶
استفاده از (اشتراکگذاری) قفل مفسر سراسریِ مفسر اصلی.
-
PyInterpreterConfig_OWN_GIL¶
از قفل مفسر سراسریِ خودِ زیرمفسر استفاده کنید.
اگر این مقدار PyInterpreterConfig_OWN_GIL باشد، آنگاه PyInterpreterConfig.use_main_obmalloc باید 0 باشد.
-
PyStatus Py_NewInterpreterFromConfig(PyThreadState **tstate_p, const PyInterpreterConfig *config)¶
یک زیرمفسر جدید ایجاد کنید. این یک محیط (تقریباً) کاملاً جداگانه برای اجرای کد پایتون است. بهویژه، مفسر جدید نسخههای جداگانه و مستقلی از تمام ماژولهای ایمپورتشده دارد، از جمله ماژولهای بنیادی builtins، __main__ و sys. جدول ماژولهای بارگذاریشده (sys.modules) و مسیر جستجوی ماژول (sys.path) نیز جداگانه هستند. محیط جدید متغیر sys.argv ندارد. این محیط اشیاء پروندهی جدید جریانهای ورودی/خروجی استاندارد sys.stdin، sys.stdout و sys.stderr را دارد (البته اینها به همان توصیفگرهای پروندهی زیرین ارجاع میدهند).
config دادهشده، گزینههایی را که مفسر با آنها راهاندازی میشود کنترل میکند.
در صورت موفقیت، tstate_p به نخستین وضعیت نخ ایجادشده در زیرمفسر جدید تنظیم میشود. این وضعیت نخ متصل است. توجه داشته باشید که هیچ نخ واقعی ایجاد نمیشود؛ بحث درباره وضعیتهای نخ را در ادامه ببینید. اگر ایجاد مفسر جدید ناموفق باشد، tstate_p به NULL تنظیم میشود؛ هیچ استثنایی تنظیم نمیشود، زیرا وضعیت استثنا در وضعیت نخ متصل ذخیره میشود که ممکن است وجود نداشته باشد.
مانند تمام توابع دیگر Python/C API، باید پیش از فراخوانی این تابع، یک attached thread state موجود باشد، اما ممکن است هنگام بازگشت جدا شده باشد. در صورت موفقیت، وضعیت نخ بازگرداندهشده متصل خواهد بود. اگر زیرمفسر (sub-interpreter) با GIL اختصاصی خودش ایجاد شود، attached thread state مفسر فراخواننده جدا خواهد شد. هنگامی که تابع بازمیگردد، thread state مفسر جدید به نخ فعلی متصل خواهد بود و attached thread state مفسر قبلی جدا باقی خواهد ماند.
اضافه شده در نسخهی 3.12.
زیرمفسرها زمانی بیشترین کارایی را دارند که از یکدیگر ایزوله باشند و برخی از قابلیتها محدود باشند:
PyInterpreterConfig config = {
.use_main_obmalloc = 0,
.allow_fork = 0,
.allow_exec = 0,
.allow_threads = 1,
.allow_daemon_threads = 0,
.check_multi_interp_extensions = 1,
.gil = PyInterpreterConfig_OWN_GIL,
};
PyThreadState *tstate = NULL;
PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config);
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
توجه داشته باشید که پیکربندی فقط بهطور گذرا استفاده میشود و تغییر داده نمیشود. در طول مقداردهی اولیه، مقادیر پیکربندی به مقادیر مختلف PyInterpreterState تبدیل میشوند. ممکن است یک نسخهی فقطخواندنی از پیکربندی بهطور داخلی روی PyInterpreterState ذخیره شود.
ماژولهای توسعهای بین (زیر-)مفسرها به شرح زیر به اشتراک گذاشته میشوند:
برای ماژولهایی که از مقداردهی اولیه چندمرحلهای استفاده میکنند، مانند PyModule_FromDefAndSpec()، یک شیء ماژول جداگانه برای هر مفسر ایجاد و مقداردهی اولیه میشود. تنها متغیرهای ایستا و سراسری در سطح C بین این اشیاء ماژول به اشتراک گذاشته میشوند.
برای ماژولهایی که از مقداردهی اولیه تکفازی قدیمی استفاده میکنند، مانند PyModule_Create()، اولین باری که یک ماژول توسعهای خاص ایمپورت میشود، بهطور عادی مقداردهی اولیه میشود و یک کپی (سطحی) از دیکشنری ماژول آن ذخیره میشود. وقتی همان ماژول توسعهای توسط (زیر)مفسر دیگری ایمپورت میشود، یک ماژول جدید مقداردهی اولیه میشود و با محتویات این کپی پر میشود؛ تابع init ماژول توسعهای فراخوانی نمیشود. بنابراین، اشیاء موجود در دیکشنری ماژول در نهایت میان (زیر)مفسرها به اشتراک گذاشته میشوند، که ممکن است باعث رفتار ناخواسته شود (به باگها و هشدارها در ادامه مراجعه کنید).
توجه داشته باشید که این مورد با آنچه هنگام ایمپورت شدن یک ماژول توسعهای پس از بازمقداردهی اولیهی کامل مفسر از طریق فراخوانی Py_FinalizeEx() و Py_Initialize() رخ میدهد، متفاوت است؛ در آن صورت، تابع initmodule ماژول توسعهای دوباره فراخوانی میشود. مانند مقداردهی اولیهی چندمرحلهای، این بدان معناست که تنها متغیرهای ایستا و سراسری در سطح C بین این ماژولها مشترک هستند.
-
PyThreadState *Py_NewInterpreter(void)¶
- قسمتی از ABI پایدار.
یک زیرمفسر (sub-interpreter) جدید ایجاد کنید. این در اصل صرفاً پوششی در اطراف Py_NewInterpreterFromConfig() با پیکربندیای است که رفتار موجود را حفظ میکند. نتیجه، یک زیرمفسر غیرمجزا (unisolated) است که قفل مفسر سراسری (GIL) را با مفسر اصلی به اشتراک میگذارد، اجازهی fork/exec را میدهد، اجازهی نخهای دِیمِن را میدهد و اجازهی ماژولهای مقداردهی اولیهی تکفازی (single-phase init) را میدهد.
-
void Py_EndInterpreter(PyThreadState *tstate)¶
- قسمتی از ABI پایدار.
(زیر)مفسری را نابود میکند که وضعیت نخ دادهشده آن را نمایندگی میکند. وضعیت نخ دادهشده باید متصل باشد. هنگامی که فراخوانی بازگردد، هیچ وضعیت نخ متصل وجود نخواهد داشت. تمام وضعیتهای نخ مرتبط با این مفسر نابود میشوند.
Py_FinalizeEx() تمام زیرمفسرهایی را که تا آن لحظه بهطور صریح نابود نشدهاند، از بین میبرد.
قفل مفسر سراسری (GIL) بهازای هر مفسر¶
اضافه شده در نسخهی 3.12.
با استفاده از Py_NewInterpreterFromConfig() میتوانید یک زیرمفسر ایجاد کنید که بهطور کامل از سایر مفسرها جداشده باشد، از جمله با داشتن قفل مفسر سراسری مخصوص به خود. مهمترین مزیت این جداسازی این است که چنین مفسری میتواند بدون مسدود شدن توسط مفسرهای دیگر و بدون مسدود کردن هیچ مفسر دیگری، کد پایتون را اجرا کند. بدین ترتیب، یک فرایند پایتون واحد میتواند هنگام اجرای کد پایتون واقعاً از چندین هستهی CPU بهره ببرد. این جداسازی همچنین بهجای صرفاً استفاده از نخها، رویکردی متفاوت به همزمانی را ترغیب میکند. (به PEP 554 و PEP 684 مراجعه کنید.)
استفاده از مفسر مجزا نیازمند هوشیاری در حفظ آن جداسازی است. این بهویژه به آن معناست که هیچ شیءای یا وضعیت تغییرپذیری را نباید بدون تضمینهایی دربارهی نخایمنی به اشتراک گذاشت. حتی اشیائی که در سایر جهات تغییرناپذیرند (مانند None و (1, 5)) بهطور معمول نمیتوانند به دلیل شمارش ارجاع به اشتراک گذاشته شوند. یک راهحل ساده اما کمکارآمدتر برای این مسئله، استفاده از یک قفل سراسری در پیرامون تمامی استفادهها از یک وضعیت (یا شیء) است. بهطور جایگزین، اشیاء عملاً تغییرناپذیر (مانند اعداد صحیح یا رشتهها) را میتوان با وجود شمارش ارجاعشان، از طریق نامیرا کردن آنها ایمن ساخت. در واقع، این کار برای تکنمونههای توکار، اعداد صحیح کوچک و تعدادی از اشیاء توکار دیگر انجام شده است.
اگر جداسازی را حفظ کنید، به محاسبات چندهستهای واقعی بدون پیچیدگیهایی که با نخبندی آزاد همراه هستند دسترسی خواهید داشت. در صورت عدم حفظ جداسازی، با تمام پیامدهای نخبندی آزاد از جمله رقابتها و فروپاشیهایی که اشکالزدایی آنها دشوار است روبهرو خواهید شد.
علاوه بر این، یکی از چالشهای اصلی استفاده از چندین مفسر جداشده این است که چگونه میتوان بهطور امن (بدون بر هم زدن جداسازی) و کارآمد بین آنها ارتباط برقرار کرد. رانتایم و کتابخانه استاندارد هنوز هیچ رویکرد استانداردی برای این کار ارائه نمیدهند. یک ماژول آینده در کتابخانه استاندارد میتواند به کاهش تلاش لازم برای حفظ جداسازی کمک کند و ابزارهای مؤثری برای ارتباط (و اشتراکگذاری) دادهها بین مفسرها در اختیار بگذارد.
اشکالها و هشدارها¶
از آنجا که زیرمفسرها (و مفسر اصلی) بخشی از یک فرایند واحد هستند، جداسازی میان آنها بینقص نیست --- برای مثال، با استفاده از عملیاتهای سطح پایین پرونده مانند os.close() میتوانند (بهطور تصادفی یا خرابکارانه) بر پروندههای باز یکدیگر تأثیر بگذارند. به دلیل نحوهی اشتراکگذاری ماژولهای توسعهای میان (زیر)مفسرها، ممکن است برخی از ماژولهای توسعهای بهدرستی کار نکنند؛ احتمال این امر بهویژه هنگام استفاده از مقداردهی اولیهی تکفازی یا متغیرهای سراسری (ایستا) بیشتر است. میتوان اشیایی را که در یک زیرمفسر ایجاد شدهاند، در فضای نام (زیر)مفسر دیگری قرار داد؛ بهتر است در صورت امکان از این کار پرهیز شود.
باید با دقت ویژه از اشتراکگذاری توابع، متدها، نمونهها یا کلاسهای تعریفشده توسط کاربر بین زیرمفسرها جلوگیری شود، زیرا عملیات ایمپورت که توسط چنین اشیایی اجرا میشوند، ممکن است بر دیکشنری ماژولهای بارگذاریشدهی (زیر)مفسر اشتباه تأثیر بگذارد. به همان اندازه مهم است که از اشتراکگذاری اشیایی که موارد فوق از طریق آنها قابل دسترسیاند، جلوگیری شود.
همچنین توجه داشته باشید که ترکیب این قابلیت با APIهای PyGILState_* امری حساس است، زیرا این APIها تناظر یکبهیک بین وضعیتهای نخ پایتون و نخهای سطح سیستمعامل را فرض میکنند؛ فرضی که با وجود زیرمفسرها نقض میشود. قویاً توصیه میشود که زیرمفسرها را بین یک جفت فراخوانی متناظر PyGILState_Ensure() و PyGILState_Release() عوض نکنید. علاوه بر این، ماژولهای توسعهای (مانند ctypes) که از این APIها برای فراهم کردن امکان فراخوانی کد پایتون از نخهایی که توسط پایتون ایجاد نشدهاند استفاده میکنند، احتمالاً هنگام استفاده از زیرمفسرها از کار میافتند.
APIهای سطحبالا¶
-
type PyInterpreterState¶
- قسمتی از ABI پایدار (بهعنوان یک ساختار مبهم).
این ساختار داده وضعیت مشترک بین تعدادی از نخهای همکاریکننده را نشان میدهد. نخهایی که به یک مفسر واحد تعلق دارند، مدیریت ماژول خود و چند مورد داخلی دیگر را به اشتراک میگذارند. در این ساختار هیچ عضو عمومی وجود ندارد.
نخهای متعلق به مفسرهای مختلف در ابتدا هیچ چیزی را به اشتراک نمیگذارند، بهجز وضعیت فرایند مانند حافظهی در دسترس، توصیفگرهای پروندهی باز و مواردی از این دست. قفل مفسر سراسری نیز میان همهی نخها مشترک است، صرفنظر از اینکه به کدام مفسر تعلق دارند.
-
PyInterpreterState *PyInterpreterState_Get(void)¶
- قسمتی از ABI پایدار از نسخهی 3.9.
مفسر جاری را برمیگرداند.
اگر attached thread state وجود نداشته باشد، خطای مهلک صادر میکند. این تابع نمیتواند NULL را بازگرداند.
-
int64_t PyInterpreterState_GetID(PyInterpreterState *interp)¶
- قسمتی از ABI پایدار از نسخهی 3.7.
شناسه یکتای مفسر را برمیگرداند. اگر در انجام این کار خطایی رخ دهد، -1 برگردانده میشود و خطا تنظیم میشود.
فراخوانکننده باید یک attached thread state داشته باشد.
-
PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp)¶
- مقدار بازگشتی: مرجع امانتی. قسمتی از ABI پایدار از نسخهی 3.8.
یک دیکشنری برمیگرداند که در آن میتوان دادههای خاص مفسر را ذخیره کرد. اگر این تابع NULL را برگرداند، هیچ استثنایی ایجاد نشده است و فراخواننده باید فرض کند که هیچ دیکشنری خاص مفسری در دسترس نیست.
این جایگزینی برای PyModule_GetState() نیست؛ ماژولهای توسعهای باید از آن برای ذخیرهسازی اطلاعات وضعیت مخصوص مفسر استفاده کنند.
دیکشنری برگرداندهشده از مفسر امانت گرفته شده است و تا زمان خاموش شدن مفسر معتبر است.
-
typedef PyObject *(*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)¶
نوع یک تابع ارزیابی فریم.
پارامتر throwflag توسط متد throw() تولیدگرها استفاده میشود: اگر ناصفر باشد، استثنای جاری مدیریت میشود.
تغییر یافته در نسخهی 3.9: این تابع اکنون پارامتر tstate را میپذیرد.
تغییر یافته در نسخهی 3.11: پارامتر frame از PyFrameObject* به _PyInterpreterFrame* تغییر کرد.
-
_PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)¶
تابع ارزیابی فریم را دریافت کنید.
به PEP 523 «افزودن یک API ارزیابی فریم به سیپایتون» مراجعه کنید.
-
void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame)¶
تابع ارزیابی فریم را تنظیم میکند.
به PEP 523 «افزودن یک API ارزیابی فریم به سیپایتون» مراجعه کنید.
-
void _PyInterpreterState_SetEvalFrameAllowSpecialization(PyInterpreterState *interp, int allow_specialization)¶
فعالسازی یا غیرفعالسازی تخصصیسازی هنگامی که یک ارزیاب قاب سفارشی فعال است.
اگر allow_specialization غیرصفر باشد، تخصیصگر تطبیقی به تخصصیسازی بایتکدها ادامه خواهد داد، حتی اگر یک تابع قاب ارزیابی سفارشی تنظیم شده باشد. وقتی allow_specialization صفر باشد، تنظیم یک قاب ارزیابی سفارشی، تخصصیسازی را غیرفعال میکند. حلقه مفسر استاندارد به خروج از بهینهسازی (deopt) در حین فعال بودن یک API ارزیابی قاب ادامه خواهد داد - تابع ارزیابی قاب باید برای بهرهبرداری از این ویژگی، آپکدهای تخصصیسازیشده را مدیریت کند.
اضافه شده در نسخهی 3.15.
-
int _PyInterpreterState_IsSpecializationEnabled(PyInterpreterState *interp)¶
اگر تخصصیسازی تطبیقی برای مفسر فعال باشد، مقدار غیرصفر برمیگرداند. تخصصیسازی زمانی فعال است که هیچ تابع قاب ارزیابی سفارشی تنظیم نشده باشد، یا زمانی که یکی با allow_specialization فعال تنظیم شده باشد.
اضافه شده در نسخهی 3.15.
©
حق نشر 2001 Python Software Foundation.
این صفحه تحت مجوز بنیاد نرمافزار پایتون نسخه ۲ منتشر شده است.
مثالها، دستورالعملها، و سایر کدهای موجود در مستندات همچنین تحت مجوز بدون بند BSD منتشر شدهاند.
برای اطلاعات بیشتر به
تاریخچه و مجوز مراجعه کنید.
بنیاد نرمافزار پایتون یک شرکت غیرانتفاعی است.
لطفاً کمک مالی کنید.
آخرین بهروزرسانی در نوامبر 12, 2026 (06:19 UTC).
آیا
اشکالی یافتید؟
ساخته شده با
Sphinx 9.1.0.