وضعیت‌های نخ و قفل مفسر سراسری

مگر در free-threaded build از CPython، مفسر پایتون به‌طور کلی نخ‌ایمن نیست. به منظور پشتیبانی از برنامه‌های چندنخی پایتون، یک قفل سراسری به نام global interpreter lock یا GIL وجود دارد که باید پیش از دسترسی به اشیای پایتون، توسط یک نخ نگه داشته شود. بدون این قفل، حتی ساده‌ترین عملیات‌ها می‌توانند در یک برنامه‌ی چندنخی مشکل ایجاد کنند: برای مثال، وقتی دو نخ هم‌زمان شمارش ارجاع یک شیء واحد را افزایش می‌دهند، ممکن است شمارش ارجاع در نهایت به جای دو بار، فقط یک بار افزایش یابد.

از این رو، تنها نخی که قفل مفسر سراسری (GIL) را در اختیار دارد، می‌تواند روی اشیای پایتون عمل کند یا C API پایتون را فراخوانی کند.

برای شبیه‌سازی همزمانی، مفسر به‌طور منظم تلاش می‌کند نخ‌ها را بین دستورالعمل‌های بایت‌کد تعویض کند (به sys.setswitchinterval() مراجعه کنید). به همین دلیل است که قفل‌ها برای نخ‌ایمنی در کد پایتون خالص نیز ضروری هستند.

علاوه بر این، قفل مفسر سراسری در اطراف عملیات ورودی/خروجی (I/O) مسدودکننده، مانند خواندن از یک پرونده یا نوشتن در آن، آزاد می‌شود. از طریق C API، این کار با جدا کردن وضعیت نخ انجام می‌شود.

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

یک نخ در هر لحظه تنها می‌تواند یک attached thread state داشته باشد. وضعیت نخ متصل معمولاً معادل با در اختیار داشتن GIL است، به‌جز در ساخت‌های نخ‌آزاد. در ساخت‌هایی که GIL فعال است، متصل کردن یک وضعیت نخ تا زمانی که بتوان GIL را به دست آورد، مسدود می‌ماند. با این حال، حتی در ساخت‌هایی که GIL غیرفعال است، همچنان داشتن یک وضعیت نخ متصل الزامی است، زیرا مفسر باید پیگیری کند که کدام نخ‌ها ممکن است به اشیای پایتون دسترسی داشته باشند.

توجه

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

به‌طور کلی، هنگام استفاده از API زبان C پایتون، همیشه یک وضعیت نخ متصل وجود خواهد داشت، از جمله در حین تعبیه و هنگام پیاده‌سازی متدها؛ بنابراین به‌ندرت پیش می‌آید که لازم باشد خودتان وضعیت نخ ایجاد کنید. تنها در برخی موارد خاص، مانند داخل بلوک Py_BEGIN_ALLOW_THREADS یا در یک نخ تازه، نخ وضعیت نخ متصل نخواهد داشت. اگر مطمئن نیستید، بررسی کنید که آیا PyThreadState_GetUnchecked() مقدار NULL برمی‌گرداند یا خیر.

اگر معلوم شد که واقعاً به ایجاد وضعیت نخ نیاز دارید، PyThreadState_New() و سپس PyThreadState_Swap() را فراخوانی کنید، یا از تابع خطرناک PyGILState_Ensure() استفاده کنید.

جدا کردن وضعیت نخ از کد توسعه‌ای

بیشتر کدهای توسعه‌ای که وضعیت نخ را دستکاری می‌کنند، ساختار ساده‌ی زیر را دارند:

وضعیت نخ را در یک متغیر محلی ذخیره کنید.
... یک عملیات ورودی/خروجی مسدودکننده انجام دهید ...
وضعیت نخ را از متغیر محلی بازیابی کنید.

این چنان رایج است که جفتی از ماکروها برای ساده‌سازی آن وجود دارد:

Py_BEGIN_ALLOW_THREADS
... یک عملیات ورودی/خروجی مسدودکننده انجام دهید ...
Py_END_ALLOW_THREADS

ماکروی Py_BEGIN_ALLOW_THREADS یک بلوک جدید باز می‌کند و یک متغیر محلی پنهان اعلان می‌کند؛ ماکروی Py_END_ALLOW_THREADS بلوک را می‌بندد.

بلوک بالا به کد زیر بسط می‌یابد:

PyThreadState *_save;

_save = PyEval_SaveThread();
... یک عملیات ورودی/خروجی مسدودکننده انجام دهید ...
PyEval_RestoreThread(_save);

نحوه کار این توابع به این صورت است:

متصل بودن وضعیت نخ نشان می‌دهد که قفل مفسر سراسری (GIL) برای مفسر نگه داشته شده است. برای جدا کردن آن، PyEval_SaveThread() فراخوانی می‌شود و نتیجه در یک متغیر محلی ذخیره می‌شود.

با جدا کردن وضعیت نخ، قفل مفسر سراسری (GIL) آزاد می‌شود که به نخ‌های دیگر اجازه می‌دهد در حالی که نخ فعلی در حال انجام عملیات ورودی/خروجی مسدودکننده است، به مفسر متصل شده و اجرا شوند. هنگامی که عملیات ورودی/خروجی به پایان رسید، وضعیت نخ قبلی با فراخوانی PyEval_RestoreThread() دوباره متصل می‌شود؛ این تابع تا زمانی که بتوان قفل مفسر سراسری را به دست آورد، منتظر می‌ماند.

توجه

انجام عملیات ورودی/خروجی مسدودکننده رایج‌ترین مورد استفاده برای جداسازی وضعیت نخ است، اما فراخوانی آن روی کد بومی طولانی‌مدتی که نیازی به دسترسی به اشیاء پایتون یا C API پایتون ندارد نیز مفید است. برای مثال، ماژول‌های استاندارد zlib و hashlib هنگام فشرده‌سازی یا هش کردن داده‌ها، وضعیت نخ را جدا می‌کنند.

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

هشدار

جدا کردن وضعیت نخ می‌تواند در حین نهایی‌سازی مفسر به رفتار غیرمنتظره منجر شود. برای جزئیات بیشتر به هشدارهایی درباره‌ی نهایی‌سازی ران‌تایم مراجعه کنید.

APIها

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

توجه

این ماکروها در ساخت‌ نخ‌آزاد همچنان برای جلوگیری از بن‌بست‌ها ضروری هستند.

Py_BEGIN_ALLOW_THREADS
قسمتی از ABI پایدار.

این ماکرو به { PyThreadState *_save; _save = PyEval_SaveThread(); بسط می‌یابد. توجه داشته باشید که این ماکرو یک آکولاد باز در بر دارد و باید با ماکروی Py_END_ALLOW_THREADS که پس از آن می‌آید جفت شود. برای بحث بیشتر درباره‌ی این ماکرو به بالا مراجعه کنید.

Py_END_ALLOW_THREADS
قسمتی از ABI پایدار.

این ماکرو به PyEval_RestoreThread(_save); } بسط می‌یابد. توجه داشته باشید که این ماکرو شامل یک آکولاد بسته است و باید با یک ماکروی Py_BEGIN_ALLOW_THREADS پیشین جفت شود. برای بحث بیشتر درباره‌ی این ماکرو، به بالا مراجعه کنید.

Py_BLOCK_THREADS
قسمتی از ABI پایدار.

این ماکرو به PyEval_RestoreThread(_save); بسط می‌یابد: معادل Py_END_ALLOW_THREADS بدون آکولاد پایانی است.

Py_UNBLOCK_THREADS
قسمتی از ABI پایدار.

این ماکرو به _save = PyEval_SaveThread(); بسط می‌یابد: معادل Py_BEGIN_ALLOW_THREADS بدون آکولاد باز و اعلان متغیر است.

نخ‌های ایجادشده خارج از پایتون

وقتی نخ‌ها با استفاده از APIهای اختصاصی پایتون (مانند ماژول threading) ایجاد می‌شوند، یک وضعیت نخ به‌طور خودکار با آن‌ها مرتبط می‌شود؛ با این حال، وقتی یک نخ از کد بومی ایجاد شود (برای مثال، توسط یک کتابخانه شخص ثالث با مدیریت نخ اختصاصی خودش)، وضعیت نخ متصل‌شده‌ای ندارد.

اگر لازم باشد کد پایتون را از این نخ‌ها فراخوانی کنید (اغلب این کار بخشی از یک API کال‌بک ارائه‌شده توسط کتابخانه شخص ثالث مذکور خواهد بود)، باید ابتدا با ایجاد یک وضعیت نخ جدید و پیوست دادن آن، این نخ‌ها را در مفسر ثبت کنید.

مقاوم‌ترین راه انجام این کار، استفاده از PyThreadState_New() و سپس PyThreadState_Swap() است.

توجه

PyThreadState_New به آرگومانی نیاز دارد که به مفسر موردنظر اشاره کند؛ چنین اشاره‌گری را می‌توان از طریق فراخوانی PyInterpreterState_Get() در کدی که نخ در آن ایجاد شده است، به دست آورد.

برای مثال:

/* مقدار بازگشتی PyInterpreterState_Get() از تابعی که
   این نخ را ایجاد کرده است. */
PyInterpreterState *interp = thread_data->interp;

/* ایجاد یک وضعیت نخ جدید برای مفسر. این وضعیت در ابتدا
   متصل نیست. */
PyThreadState *tstate = PyThreadState_New(interp);

/* اتصال وضعیت نخ، که قفل مفسر سراسری را به دست خواهد آورد. */
PyThreadState_Swap(tstate);

/* اینجا کنش‌های پایتون را انجام دهید. */
result = CallSomeFunction();
/* ارزیابی نتیجه یا مدیریت استثنا */

/* تخریب وضعیت نخ. پس از این نقطه هیچ API پایتونی مجاز نیست. */
PyThreadState_Clear(tstate);
PyThreadState_DeleteCurrent();

هشدار

اگر مفسر پیش از فراخوانی PyThreadState_Swap نهایی‌سازی شده باشد، آنگاه interp یک اشاره‌گر معلق (dangling pointer) خواهد بود!

API قدیمی

الگوی رایج دیگر برای فراخوانی کد پایتون از یک نخ غیرپایتونی، استفاده از PyGILState_Ensure() و سپس فراخوانی PyGILState_Release() است.

این توابع زمانی که چندین مفسر در فرایند پایتون وجود داشته باشند، به‌خوبی کار نمی‌کنند. اگر تاکنون هیچ مفسر پایتونی در نخ فعلی استفاده نشده باشد (که این امر برای نخ‌هایی که خارج از پایتون ایجاد شده‌اند رایج است)، PyGILState_Ensure یک وضعیت نخ برای مفسر «اصلی» (اولین مفسر در فرایند پایتون) ایجاد و پیوست می‌دهد.

علاوه بر این، این توابع در حین نهایی‌سازی مفسر دارای مشکلات نخ‌ایمنی هستند. استفاده از PyGILState_Ensure در حین نهایی‌سازی به احتمال زیاد باعث فروپاشی فرایند می‌شود.

استفاده از این توابع به این شکل است:

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

/* Perform Python actions here. */
result = CallSomeFunction();
/* evaluate result or handle exception */

/* Release the thread. No Python API allowed beyond this point. */
PyGILState_Release(gstate);

هشدارهایی درباره‌ی fork()

نکته‌ی مهم دیگری که درباره‌ی نخ‌ها باید به آن توجه کرد، رفتار آن‌ها در برابر فراخوانی C fork() است. در بیشتر سیستم‌هایی که fork() را دارند، پس از انشعاب یک فرایند، تنها نخی که انشعاب را انجام داده است، وجود خواهد داشت. این موضوع هم بر نحوه‌ی مدیریت قفل‌ها و هم بر تمام وضعیت ذخیره‌شده در ران‌تایم سی‌پایتون تأثیر ملموسی دارد.

این واقعیت که فقط نخ «فعلی» باقی می‌ماند، به این معناست که هر قفلی که توسط نخ‌های دیگر نگه‌داشته شده است، هرگز آزاد نخواهد شد. پایتون این مشکل را برای os.fork() با گرفتن قفل‌های داخلی خود پیش از انشعاب و آزاد کردن آن‌ها پس از آن حل می‌کند. علاوه بر این، پایتون هر اشیای قفل را در فرزند بازنشانی می‌کند. هنگام توسعه یا تعبیه پایتون، هیچ راهی برای اطلاع دادن به پایتون درباره‌ی قفل‌های اضافی (غیرپایتونی) که باید پیش از انشعاب گرفته شوند یا پس از آن بازنشانی شوند، وجود ندارد. برای انجام همین کار، لازم است از امکانات سیستم‌عامل مانند pthread_atfork() استفاده شود. افزون بر این، هنگام توسعه یا تعبیه پایتون، فراخوانی مستقیم fork() به‌جای فراخوانی آن از طریق os.fork() (و بازگشت به پایتون یا فراخوانی کدهای پایتون) ممکن است به بن‌بست منجر شود؛ زیرا یکی از قفل‌های داخلی پایتون توسط نخی نگه‌داشته می‌شود که پس از انشعاب از کار افتاده است. PyOS_AfterFork_Child() می‌کوشد قفل‌های لازم را بازنشانی کند، اما همیشه قادر به انجام آن نیست.

این واقعیت که همه نخ‌های دیگر از بین می‌روند، همچنین به این معناست که وضعیت ران‌تایم سی‌پایتون در آن‌جا باید به‌درستی پاک‌سازی شود، که os.fork() این کار را انجام می‌دهد. این به معنای نهایی کردن همه اشیاء دیگر PyThreadState متعلق به مفسر فعلی و همه اشیاء دیگر PyInterpreterState است. به دلیل این موضوع و ماهیت خاص مفسر «اصلی»، fork() باید فقط در نخ «اصلی» آن مفسر فراخوانی شود، جایی که ران‌تایم سراسری سی‌پایتون در ابتدا راه‌اندازی شده بود. تنها استثنا زمانی است که exec() بلافاصله پس از آن فراخوانی شود.

APIهای سطح‌بالا

این‌ها پرکاربردترین نوع‌ها و توابع هنگام نوشتن توسعه‌های C چندنخی هستند.

type PyThreadState
قسمتی از API محدود (به‌عنوان یک ساختار مبهم).

این ساختار داده، وضعیت یک نخ منفرد را نشان می‌دهد. تنها عضو داده‌ی عمومی عبارت است از:

PyInterpreterState *interp

وضعیت مفسر این نخ.

void PyEval_InitThreads()
قسمتی از ABI پایدار.

تابع منسوخ که هیچ کاری انجام نمی‌دهد.

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

تغییر یافته در نسخه‌ی 3.9: تابع اکنون هیچ کاری انجام نمی‌دهد.

تغییر یافته در نسخه‌ی 3.7: این تابع اکنون توسط Py_Initialize() فراخوانی می‌شود، بنابراین دیگر لازم نیست خودتان آن را فراخوانی کنید.

تغییر یافته در نسخه‌ی 3.2: این تابع دیگر نمی‌تواند پیش از Py_Initialize() فراخوانی شود.

منسوخ شده از نسخه‌ی 3.9.

PyThreadState *PyEval_SaveThread()
قسمتی از ABI پایدار.

attached thread state را جدا کنید و آن را برگردانید. پس از بازگشت، نخ هیچ thread state نخواهد داشت.

void PyEval_RestoreThread(PyThreadState *tstate)
قسمتی از ABI پایدار.

وضعیت نخ متصل را به tstate تنظیم کنید. وضعیت نخ ارسال‌شده نباید متصل باشد، در غیر این صورت بن‌بست رخ می‌دهد. پس از بازگشت، tstate متصل خواهد شد.

توجه

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

تغییر یافته در نسخه‌ی 3.14: اگر در حین نهایی‌سازی مفسر فراخوانی شود، نخ فعلی را معلق می‌کند، نه اینکه آن را خاتمه دهد.

PyThreadState *PyThreadState_Get()
قسمتی از ABI پایدار.

attached thread state را برمی‌گرداند. اگر نخ وضعیت نخ متصلی نداشته باشد (مانند زمانی که درون بلوک Py_BEGIN_ALLOW_THREADS هستیم)، خطای مهلک ایجاد می‌شود (تا فراخواننده نیازی به بررسی NULL نداشته باشد).

همچنین PyThreadState_GetUnchecked() را ببینید.

PyThreadState *PyThreadState_GetUnchecked()

مشابه PyThreadState_Get()، اما اگر NULL باشد، فرایند را با خطای مهلک از بین نمی‌برد. فراخواننده مسئول بررسی NULL بودن نتیجه است.

اضافه شده در نسخه‌ی 3.13: در پایتون 3.5 تا 3.12، این تابع خصوصی بود و به نام _PyThreadState_UncheckedGet() شناخته می‌شد.

PyThreadState *PyThreadState_Swap(PyThreadState *tstate)
قسمتی از ABI پایدار.

attached thread state را روی tstate تنظیم می‌کند و thread state را که پیش از فراخوانی متصل بوده است، بازمی‌گرداند.

فراخوانی این تابع بدون وضعیت نخ متصل ایمن است؛ این تابع صرفاً NULL را برمی‌گرداند که نشان می‌دهد هیچ وضعیت نخ پیشینی وجود نداشته است.

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

PyEval_ReleaseThread()

توجه

مشابه PyGILState_Ensure()، این تابع در صورتی که ران‌تایم در حال نهایی‌سازی باشد، نخ را معلق می‌کند.

APIهای وضعیت قفل مفسر سراسری

توابع زیر از ذخیره‌سازی نخ‌محلی استفاده می‌کنند و با زیرمفسر‌ها (sub-interpreters) سازگار نیستند:

type PyGILState_STATE
قسمتی از ABI پایدار.

نوع مقداری که توسط PyGILState_Ensure() بازگردانده می‌شود و به PyGILState_Release() پاس داده می‌شود.

enumerator PyGILState_LOCKED

هنگام فراخوانی PyGILState_Ensure()، قفل مفسر سراسری از قبل گرفته شده بود.

enumerator PyGILState_UNLOCKED

قفل مفسر سراسری هنگام فراخوانی PyGILState_Ensure() در اختیار گرفته نشده بود.

PyGILState_STATE PyGILState_Ensure()
قسمتی از ABI پایدار.

تضمین می‌کند که نخ فعلی، صرف‌نظر از وضعیت فعلی پایتون یا وضعیت نخ متصل، برای فراخوانی Python C API آماده باشد. یک نخ می‌تواند این تابع را به هر تعداد که مطلوب است فراخوانی کند، به شرط آن‌که هر فراخوانی با یک فراخوانی PyGILState_Release() جفت شود. به‌طور کلی، می‌توان بین فراخوانی‌های PyGILState_Ensure() و PyGILState_Release() از سایر APIهای مربوط به نخ استفاده کرد، به شرط آن‌که وضعیت نخ پیش از Release() به وضعیت پیشین خود بازگردانی شود. برای نمونه، استفاده‌ی معمول از ماکروهای Py_BEGIN_ALLOW_THREADS و Py_END_ALLOW_THREADS قابل‌قبول است.

مقدار بازگشتی، یک «دسته» مات برای attached thread state در زمان فراخوانی PyGILState_Ensure() است و باید به PyGILState_Release() پاس داده شود تا اطمینان حاصل شود که پایتون در همان وضعیت باقی می‌ماند. هرچند فراخوانی‌های بازگشتی مجاز هستند، این دسته‌ها نمی‌توانند به اشتراک گذاشته شوند؛ هر فراخوانی مجزای PyGILState_Ensure() باید دسته را برای فراخوانی خود به PyGILState_Release() ذخیره کند.

هنگامی که تابع بازمی‌گردد، یک attached thread state وجود خواهد داشت و نخ قادر خواهد بود کد دلخواه پایتون را فراخوانی کند. شکست یک خطای مهلک است.

هشدار

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

تغییر یافته در نسخه‌ی 3.14: اگر در حین نهایی‌سازی مفسر فراخوانی شود، نخ فعلی را معلق می‌کند، نه اینکه آن را خاتمه دهد.

void PyGILState_Release(PyGILState_STATE)
قسمتی از ABI پایدار.

هر منبعی که پیش‌تر به‌دست آمده است را آزاد می‌کند. پس از این فراخوانی، وضعیت پایتون همان وضعیتی خواهد بود که پیش از فراخوانی متناظر PyGILState_Ensure() وجود داشت (اما به‌طور کلی این وضعیت برای فراخواننده ناشناخته است، از این رو از GILState API استفاده می‌شود).

هر فراخوانی PyGILState_Ensure() باید با یک فراخوانی PyGILState_Release() روی همان نخ متناظر باشد.

PyThreadState *PyGILState_GetThisThreadState()
قسمتی از ABI پایدار.

وضعیت نخ متصل مربوط به این نخ را برمی‌گرداند. اگر هیچ API‌ی GILState روی نخ فعلی استفاده نشده باشد، ممکن است NULL را برگرداند. توجه داشته باشید که نخ اصلی همیشه چنین وضعیت نخی دارد، حتی اگر هیچ فراخوانی auto-thread-state روی نخ اصلی انجام نشده باشد. این عمدتاً یک تابع کمکی/تشخیصی است.

توجه

این تابع ممکن است حتی زمانی که thread state جدا شده باشد، مقدار غیر NULL را برگرداند. برای بیشتر موارد، PyThreadState_Get() یا PyThreadState_GetUnchecked() را ترجیح دهید.

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

PyThreadState_Get()

int PyGILState_Check()

اگر نخ فعلی قفل مفسر سراسری را در اختیار داشته باشد، 1 و در غیر این صورت 0 را برمی‌گرداند. این تابع را می‌توان در هر زمان از هر نخی فراخوانی کرد. تنها در صورتی 1 برمی‌گرداند که وضعیت نخ آن از طریق PyGILState_Ensure() مقداردهی اولیه شده باشد. این عمدتاً یک تابع کمکی/تشخیصی است. این تابع می‌تواند برای مثال در زمینه‌های کال‌بک یا توابع تخصیص حافظه مفید باشد؛ زمانی که دانستن اینکه قفل مفسر سراسری گرفته شده است می‌تواند به فراخوان‌کننده اجازه دهد تا کنش‌های حساسی انجام دهد یا رفتار متفاوتی داشته باشد.

توجه

اگر فرایند فعلی پایتون تا به حال یک زیرمفسر ایجاد کرده باشد، این تابع همیشه 1 را برمی‌گرداند. در بیشتر موارد، PyThreadState_GetUnchecked() را ترجیح دهید.

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

APIهای سطح پایین

PyThreadState *PyThreadState_New(PyInterpreterState *interp)
قسمتی از ABI پایدار.

یک شیء وضعیت نخ جدید متعلق به شیء مفسر داده‌شده ایجاد کنید. به یک وضعیت نخ متصل نیازی نیست.

void PyThreadState_Clear(PyThreadState *tstate)
قسمتی از ABI پایدار.

تمام اطلاعات در یک شیء وضعیت نخ را بازنشانی می‌کند. tstate باید متصل باشد

تغییر یافته در نسخه‌ی 3.9: این تابع اکنون کال‌بک PyThreadState.on_delete را فراخوانی می‌کند. پیش‌تر، این کار در PyThreadState_Delete() انجام می‌شد.

تغییر یافته در نسخه‌ی 3.13: کال‌بک PyThreadState.on_delete حذف شد.

void PyThreadState_Delete(PyThreadState *tstate)
قسمتی از ABI پایدار.

یک شیء وضعیت نخ را نابود می‌کند. tstate نباید به هیچ نخی متصل باشد. tstate باید پیش‌تر با فراخوانی PyThreadState_Clear() بازنشانی شده باشد.

void PyThreadState_DeleteCurrent(void)

وضعیت نخ متصل را جدا کنید (که باید پیش‌تر با یک فراخوانی از PyThreadState_Clear() بازنشانی شده باشد) و سپس آن را نابود کنید.

پس از بازگشت، هیچ thread stateای پیوست نخواهد شد.

PyFrameObject *PyThreadState_GetFrame(PyThreadState *tstate)
قسمتی از ABI پایدار از نسخه‌ی 3.10.

گرفتن فریم فعلی وضعیت نخ پایتون tstate.

یک ارجاع قوی را برمی‌گرداند. اگر هیچ فریمی هم‌اکنون در حال اجرا نباشد، NULL را برمی‌گرداند.

همچنین ببینید PyEval_GetFrame().

tstate نباید NULL باشد، و باید متصل باشد.

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

uint64_t PyThreadState_GetID(PyThreadState *tstate)
قسمتی از ABI پایدار از نسخه‌ی 3.10.

شناسه‌ی یکتای thread state را از وضعیت نخ پایتون tstate دریافت کنید.

tstate نباید NULL باشد، و باید متصل باشد.

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

PyInterpreterState *PyThreadState_GetInterpreter(PyThreadState *tstate)
قسمتی از ABI پایدار از نسخه‌ی 3.10.

مفسر وضعیت نخ پایتون tstate را برمی‌گرداند.

tstate نباید NULL باشد، و باید متصل باشد.

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

void PyThreadState_EnterTracing(PyThreadState *tstate)

ردگیری و پروفایل‌گیری را در وضعیت نخ پایتون tstate تعلیق می‌کند.

برای از سر گرفتن آن‌ها، از تابع PyThreadState_LeaveTracing() استفاده کنید.

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

void PyThreadState_LeaveTracing(PyThreadState *tstate)

ردگیری و پروفایل‌گیری را در وضعیت نخ پایتون tstate که توسط تابع PyThreadState_EnterTracing() معلق شده است، از سر می‌گیرد.

همچنین توابع PyEval_SetTrace() و PyEval_SetProfile() را ببینید.

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

int PyUnstable_ThreadState_SetStackProtection(PyThreadState *tstate, void *stack_start_addr, size_t stack_size)
این است API ناپایداراین ممکن است بدون هشدار در نسخه‌های جزئی تغییر کند.

آدرس شروع حفاظت پشته و اندازه حفاظت پشته‌ی یک وضعیت نخ پایتون را تنظیم می‌کند.

در صورت موفقیت، 0 برمی‌گرداند. در صورت شکست، یک استثنا تنظیم کرده و -1 برمی‌گرداند.

سی‌پایتون کنترل بازگشت را برای کد C به این صورت پیاده‌سازی می‌کند که وقتی متوجه می‌شود که پشته اجرای ماشین نزدیک به سرریز است، RecursionError ایجاد می‌کند. برای نمونه، تابع Py_EnterRecursiveCall() را ببینید. برای این کار، سی‌پایتون باید مکان پشته نخ فعلی را بداند که معمولاً آن را از سیستم‌عامل دریافت می‌کند. هنگامی که پشته تغییر می‌کند، مثلاً با استفاده از تکنیک‌های تعویض زمینه مانند boost::context در کتابخانه Boost، باید PyUnstable_ThreadState_SetStackProtection() را فراخوانی کنید تا سی‌پایتون را از این تغییر مطلع کنید.

تابع PyUnstable_ThreadState_SetStackProtection() را یا پیش از تغییر پشته یا پس از آن فراخوانی کنید. بین این فراخوانی و تغییر پشته، هیچ فراخوانی دیگری به C API پایتون انجام ندهید.

برای بازگردانی این عملیات، به PyUnstable_ThreadState_ResetStackProtection() مراجعه کنید.

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

void PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate)
این است API ناپایداراین ممکن است بدون هشدار در نسخه‌های جزئی تغییر کند.

نشانی شروع حفاظت پشته و اندازه حفاظت پشته وضعیت نخ پایتون را به پیش‌فرض‌های سیستم‌عامل بازنشانی می‌کند.

برای توضیح به PyUnstable_ThreadState_SetStackProtection() مراجعه کنید.

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

PyObject *PyThreadState_GetDict()
مقدار بازگشتی: مرجع امانتی. قسمتی از ABI پایدار.

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

void PyEval_AcquireThread(PyThreadState *tstate)
قسمتی از ABI پایدار.

tstate را، که نباید NULL یا از قبل پیوست‌شده باشد، به نخ فعلی پیوست می‌کند.

نخ فراخواننده نباید از قبل یک attached thread state داشته باشد.

توجه

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

تغییر یافته در نسخه‌ی 3.8: به‌روزرسانی شد تا با PyEval_RestoreThread()، Py_END_ALLOW_THREADS() و PyGILState_Ensure() سازگار باشد و اگر در حین نهایی‌سازی مفسر فراخوانی شود، نخ جاری را خاتمه می‌دهد.

تغییر یافته در نسخه‌ی 3.14: اگر در حین نهایی‌سازی مفسر فراخوانی شود، نخ فعلی را معلق می‌کند، نه اینکه آن را خاتمه دهد.

PyEval_RestoreThread() تابعی در سطح بالاتر است که همیشه در دسترس است (حتی زمانی که نخ‌ها مقداردهی اولیه نشده باشند).

void PyEval_ReleaseThread(PyThreadState *tstate)
قسمتی از ABI پایدار.

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

PyEval_SaveThread() تابعی در سطح بالاتر است که همیشه دسترس‌پذیر است (حتی زمانی که نخ‌ها مقداردهی اولیه نشده باشند).

اطلاع‌رسانی ناهمگام

سازوکاری برای ارسال اعلان‌های ناهمگام به نخ اصلی مفسر ارائه شده است. این اعلان‌ها به شکل یک اشاره‌گر تابع و یک آرگومان اشاره‌گر void هستند.

int Py_AddPendingCall(int (*func)(void*), void *arg)
قسمتی از ABI پایدار.

یک تابع را برای فراخوانی از نخ اصلی مفسر زمان‌بندی می‌کند. در صورت موفقیت، 0 بازگردانده می‌شود و func برای فراخوانی در نخ اصلی در صف قرار می‌گیرد. در صورت شکست، -1 بدون تنظیم هیچ استثنایی بازگردانده می‌شود.

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

  • در مرز بایت‌کد؛

  • در حالی که نخ اصلی یک وضعیت نخ متصل را در اختیار دارد (func در نتیجه می‌تواند از تمام API زبان C استفاده کند).

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

این تابع به وضعیت نخ متصل نیاز ندارد. با این حال، برای فراخوانی این تابع در یک زیرمفسر، فراخواننده باید وضعیت نخ متصل داشته باشد. در غیر این صورت، ممکن است تابع func برای فراخوانی از مفسر اشتباه زمان‌بندی شود.

هشدار

این یک تابع سطح پایین است که فقط برای موارد بسیار خاص مفید است. هیچ تضمینی وجود ندارد که func در سریع‌ترین حالت ممکن فراخوانی شود. اگر نخ اصلی مشغول اجرای یک فراخوانی سیستمی باشد، func پیش از بازگشت فراخوانی سیستمی فراخوانی نخواهد شد. این تابع به‌طور کلی برای فراخوانی کد پایتون از نخ‌های C دلخواه مناسب نیست. در عوض، از PyGILState API استفاده کنید.

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

تغییر یافته در نسخه‌ی 3.9: اگر این تابع در یک زیرمفسر فراخوانی شود، تابع func اکنون برای فراخوانی از طریق زیرمفسر زمان‌بندی می‌شود، نه از طریق مفسر اصلی. هر زیرمفسر اکنون فهرست فراخوانی‌های زمان‌بندی‌شده‌ی خود را دارد.

تغییر یافته در نسخه‌ی 3.12: این تابع اکنون همیشه func را برای اجرا در مفسر اصلی زمان‌بندی می‌کند.

int Py_MakePendingCalls(void)
قسمتی از ABI پایدار.

همه‌ی فراخوانی‌های در انتظار را اجرا می‌کند. این کار معمولاً به‌طور خودکار توسط مفسر اجرا می‌شود.

این تابع در صورت موفقیت 0 را بازمی‌گرداند و در صورت شکست -1 را به‌همراه یک استثنای تنظیم‌شده بازمی‌گرداند.

اگر این تابع در نخ اصلی مفسر اصلی فراخوانی نشود، این تابع هیچ کاری انجام نمی‌دهد و 0 را برمی‌گرداند. فراخواننده باید یک attached thread state را در اختیار داشته باشد.

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

تغییر یافته در نسخه‌ی 3.12: این تابع فقط فراخوانی‌های در انتظار را در مفسر اصلی اجرا می‌کند.

int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)
قسمتی از ABI پایدار.

یک استثنا را به‌صورت ناهمگام در یک نخ ایجاد می‌کند. آرگومان id شناسه‌ی نخ هدف است؛ exc شیء استثنا است که باید ایجاد شود. این تابع هیچ ارجاعی به exc را سرقت نمی‌کند. برای جلوگیری از سوءاستفاده‌ی ساده‌لوحانه، باید برای فراخوانی این تابع، افزونه‌ی C خودتان را بنویسید. باید با یک وضعیت نخ متصل فراخوانی شود. تعداد وضعیت‌های نخ که تغییر کرده‌اند را برمی‌گرداند؛ این تعداد معمولاً یکی است، اما اگر شناسه‌ی نخ پیدا نشود، صفر خواهد بود. اگر exc برابر NULL باشد، استثنای در انتظار (در صورت وجود) برای آن نخ پاک می‌شود. این تابع هیچ استثنایی ایجاد نمی‌کند.

تغییر یافته در نسخه‌ی 3.7: نوع پارامتر id از long به unsigned long تغییر یافت.

APIهای نخ سیستم‌عامل

PYTHREAD_INVALID_THREAD_ID

مقدار نشانگر برای شناسه‌ی نامعتبر نخ.

این در حال حاضر معادل (unsigned long)-1 است.

unsigned long PyThread_start_new_thread(void (*func)(void*), void *arg)
قسمتی از ABI پایدار.

تابع func را با آرگومان arg در یک نخ جدید آغاز می‌کند. نخ حاصل برای پیوستن در نظر گرفته نشده است.

func نباید NULL باشد، اما arg می‌تواند NULL باشد.

در صورت موفقیت، این تابع شناسه‌ی نخ جدید را برمی‌گرداند؛ در صورت شکست، PYTHREAD_INVALID_THREAD_ID را برمی‌گرداند.

فراخواننده نیازی به در اختیار داشتن attached thread state ندارد.

unsigned long PyThread_get_thread_ident(void)
قسمتی از ABI پایدار.

شناسه‌ی نخ فعلی را برمی‌گرداند که هرگز صفر نخواهد بود.

این تابع نمی‌تواند شکست بخورد و فراخوانی‌کننده نیازی به نگه داشتن attached thread state ندارد.

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

threading.get_ident()

PyObject *PyThread_GetInfo(void)
قسمتی از ABI پایدار از نسخه‌ی 3.3.

اطلاعات کلی درباره‌ی نخ فعلی را به شکل یک شیء دنباله ساختاری دریافت کنید. این اطلاعات در پایتون به صورت sys.thread_info در دسترس است.

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

فراخواننده باید یک attached thread state را در اختیار داشته باشد.

PY_HAVE_THREAD_NATIVE_ID

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

unsigned long PyThread_get_thread_native_id(void)
قسمتی از ABI پایدار on platforms with native thread IDs.

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

این تابع تنها زمانی دسترس‌پذیر است که PY_HAVE_THREAD_NATIVE_ID تعریف‌شده باشد.

این تابع نمی‌تواند شکست بخورد و فراخوانی‌کننده نیازی به نگه داشتن attached thread state ندارد.

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

threading.get_native_id()

void PyThread_exit_thread(void)
قسمتی از ABI پایدار.

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

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

هشدار

اگر سیستم فعلی از نخ‌های POSIX (که با نام «pthreads» نیز شناخته می‌شوند) استفاده کند، این تابع pthread_exit(3) را فراخوانی می‌کند که تلاش می‌کند واپیمایش پشته (stack unwinding) را انجام دهد و در برخی پیاده‌سازی‌های libc مخرب‌های C++ را فراخوانی کند. اما اگر به یک تابع noexcept برسد، ممکن است فرایند را خاتمه دهد. سایر سیستم‌ها مانند macOS واپیمایش پشته را انجام می‌دهند.

در ویندوز، این تابع _endthreadex() را فراخوانی می‌کند که نخ را بدون فراخوانی مخرب‌های C++ می‌کشد.

در هر صورت، خطر خرابی پشته‌ی نخ وجود دارد.

منسوخ شده از نسخه‌ی 3.14.

void PyThread_init_thread(void)
قسمتی از ABI پایدار.

APIهای PyThread* را مقداردهی اولیه می‌کند. پایتون این تابع را به‌طور خودکار اجرا می‌کند، بنابراین نیاز چندانی به فراخوانی آن از یک ماژول توسعه‌ای وجود ندارد.

int PyThread_set_stacksize(size_t size)
قسمتی از ABI پایدار.

اندازه پشته‌ی نخ فعلی را روی size بایت تنظیم می‌کند.

این تابع در صورت موفقیت 0، در صورت نامعتبر بودن size مقدار -1، یا در صورتی که سیستم از تغییر اندازه پشته پشتیبانی نکند -2 برمی‌گرداند. این تابع استثنایی تنظیم نمی‌کند.

فراخواننده نیازی به در اختیار داشتن attached thread state ندارد.

size_t PyThread_get_stacksize(void)
قسمتی از ABI پایدار.

اندازه پشته نخ فعلی را بر حسب بایت برمی‌گرداند، یا 0 اگر اندازه پشته پیش‌فرض سیستم در حال استفاده باشد.

فراخواننده نیازی به در اختیار داشتن attached thread state ندارد.