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

مگر در 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 برمی‌گرداند یا خیر.

If it turns out that you do need to create a thread state, it is recommended to use PyThreadState_Ensure() or PyThreadState_EnsureFromView(), which will manage the thread state for you.

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

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

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

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

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) معمولاً منتفی است، اما جدا کردن وضعیت نخ همچنان الزامی است، زیرا مفسر به‌طور دوره‌ای نیاز دارد همه‌ی نخ‌ها را مسدود کند تا نمای سازگار از اشیای پایتون را بدون خطر شرایط رقابتی به دست آورد. برای مثال، سی‌پایتون در حال حاضر هنگام اجرای زباله‌روب، همه‌ی نخ‌ها را برای مدت کوتاهی معلق می‌کند.

هشدار

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

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 بدون آکولاد باز و اعلان متغیر است.

Using the C API from foreign threads

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

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

The easiest way to do this is through PyThreadState_Ensure() or PyThreadState_EnsureFromView().

توجه

These functions require an argument pointing to the desired interpreter; such a pointer can be acquired via a call to PyInterpreterGuard_FromCurrent() (for PyThreadState_Ensure) or PyInterpreterView_FromCurrent() (for PyThreadState_EnsureFromView) from the function that creates the thread. If no pointer is available (such as when the given native thread library doesn't provide a data argument), PyInterpreterView_FromMain() can be used to get a view for the main interpreter, but note that this will make the code incompatible with subinterpreters.

برای مثال:

// The return value of PyInterpreterGuard_FromCurrent() from the
// function that created this thread.
PyInterpreterGuard *guard = thread_data->guard;

// Create a new thread state for the interpreter.
PyThreadStateToken *token = PyThreadState_Ensure(guard);
if (token == NULL) {
   PyInterpreterGuard_Close(guard);
   return;
}

// We have a valid thread state -- perform Python actions here.
result = CallSomeFunction();
// Evaluate result or handle exceptions.

// Release the thread state. No calls to the C API are allowed beyond this
// point.
PyThreadState_Release(token);
PyInterpreterGuard_Close(guard);

Keep in mind that calling PyThreadState_Ensure might not always create a new thread state, and calling PyThreadState_Release might not always detach it. These functions may reuse an existing attached thread state, or may re-attach a thread state that was previously attached for the current thread.

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

PEP 788

Reusing a thread state across repeated calls

Creating and destroying a PyThreadState is not free, and is more expensive on a free-threaded build. A foreign thread that calls into the interpreter many times -- for example, a worker thread in a native thread pool -- should avoid creating a fresh thread state on every entry and destroying it on every exit. Instead, set up one thread state when the thread starts (or lazily on its first call into Python), attach and detach it around each call, and tear it down once when the thread exits.

Manage the thread state explicitly with PyThreadState_New(), attaching and detaching it with PyEval_RestoreThread() and PyEval_SaveThread(). This happens in three distinct phases, at different points in the thread's life.

When the thread starts, create one thread state for it. interp is the target interpreter, captured by the code that created this thread while it held an attached thread state (for example via PyInterpreterState_Get()):

PyThreadState *tstate = PyThreadState_New(interp);

Then, on each call into Python -- which may happen many times over the thread's life -- attach the thread state, make the Python C API calls that require it, and detach again so the thread does not hold the GIL while off doing non-Python work:

PyEval_RestoreThread(tstate);
result = CallSomeFunction();  /* your Python C API calls go here */
PyEval_SaveThread();

When the thread is finished calling into Python, destroy the thread state once:

PyEval_RestoreThread(tstate);
PyThreadState_Clear(tstate);
PyThreadState_DeleteCurrent();

The general-purpose entry points for calling in from a foreign thread -- PyThreadState_Ensure() and the older PyGILState_Ensure() -- do not guarantee a persistent thread state: their thread-state lifetime is deliberately implementation-defined, so a matched acquire/release pair may create and destroy a thread state each time. Use PyThreadState_New(), as shown here, whenever you specifically want to reuse one thread state across calls.

The code that created the foreign thread must arrange for the shutdown sequence to run before the thread exits, and before Py_FinalizeEx() is called. If interpreter finalization begins first, the shutdown PyEval_RestoreThread() call will hang the thread rather than return (see Cautions regarding interpreter finalization). If the thread exits without running the shutdown sequence, the thread state is leaked for the remainder of the process.

Attaching/detaching thread states

PyThreadStateToken *PyThreadState_Ensure(PyInterpreterGuard *guard)
قسمتی از ABI پایدار از نسخه‌ی 3.15.

Ensure that the thread has an attached thread state for the interpreter protected by guard, and thus can safely invoke that interpreter.

It is OK to call this function if the thread already has an attached thread state, as long as there is a subsequent call to PyThreadState_Release() that matches this one (meaning that "nested" calls to this function are permitted).

The function's effect (if any) will be reversed by the matching call to PyThreadState_Release().

On error, this function returns NULL without an exception set. Do not call PyThreadState_Release() in this case.

On success, this function returns a pointer value that must be passed to the matching call to PyThreadState_Release().

The conditions in which this function creates a new thread state are considered unstable and implementation-dependent. If you need to control the exact lifetime of a thread state, consider using PyThreadState_New(). However, do not avoid this function solely on the basis that the lifetime of the thread state may be inconsistent across versions; changes to this function will be done with caution and in a backwards-compatible manner. In particular, the saving of thread-local variables and similar state will be retained across Python versions.

جزئیات پیاده‌سازی در CPython: The exact behavior of whether this function creates a new thread state is described below, but be aware that this may change in the future.

First, this function checks if an attached thread state is present. If there is, this function then checks if the interpreter of that thread state matches the interpreter guarded by guard. If that is the case, this function simply marks the thread state as being used by a PyThreadState_Ensure call and returns.

If there is no attached thread state, then this function checks if any thread state has been used by the current OS thread. (This is returned by PyGILState_GetThisThreadState().) If there was, then this function checks if that thread state's interpreter matches guard. If it does, it is re-attached and marked as used.

Otherwise, if both of the above cases fail, a new thread state is created for guard. It is then attached and marked as owned by PyThreadState_Ensure.

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

PyThreadStateToken *PyThreadState_EnsureFromView(PyInterpreterView *view)
قسمتی از ABI پایدار از نسخه‌ی 3.15.

Get an attached thread state for the interpreter referenced by view.

The behavior and return value are the same as for PyThreadState_Ensure(); additionally, if the function succeeds, the interpreter referenced by view will be implicitly guarded. The guard will be released upon the corresponding PyThreadState_Release() call.

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

void PyThreadState_Release(PyThreadStateToken *token)
قسمتی از ABI پایدار از نسخه‌ی 3.15.

Undo a PyThreadState_Ensure() or PyThreadState_EnsureFromView() call.

This must be called exactly once for each successful Ensure call, with token set to that call's return value.

The state that was attached before the corresponding Ensure call (if any) will be attached when PyThreadState_Release() returns.

The exact behavior of whether this function deletes a thread state is considered unstable and implementation-dependent.

جزئیات پیاده‌سازی در CPython: Currently, this function will decrement an internal counter on the attached thread state. If this counter ever reaches below zero, this function emits a fatal error (via Py_FatalError()).

If the attached thread state is owned by PyThreadState_Ensure, then the attached thread state will be deallocated and deleted upon the internal counter reaching zero. Otherwise, nothing happens when the counter reaches zero.

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

type PyThreadStateToken
قسمتی از ABI پایدار (به‌عنوان یک ساختار مبهم) از نسخه‌ی 3.15.

An opaque token retrieved from a PyThreadState_Ensure() call and passed to a corresponding PyThreadState_Release() call.

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

The following APIs are generally not compatible with subinterpreters and will hang the process during interpreter finalization (see Cautions regarding interpreter finalization). As such, these APIs were soft deprecated in Python 3.15 in favor of the new APIs.

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() ذخیره کند.

When the function returns, there will be an attached thread state and the thread will be able to call arbitrary Python code.

This function has no way to return an error. As such, errors are either fatal (that is, they send SIGABRT and crash the process; see Py_FatalError()), or the thread will be permanently blocked (such as during interpreter finalization).

هشدار

Calling this function when the interpreter is finalizing will infinitely hang the thread, which may cause deadlocks. Cautions regarding interpreter finalization for more details.

In addition, this function generally does not work with subinterpreters when used from foreign threads, because this function has no way of knowing which interpreter created the thread (and as such, will implicitly pick the main interpreter).

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

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

Release any resources previously acquired. After this call, Python's state will be the same as it was prior to the corresponding PyGILState_Ensure() call (but generally this state will be unknown to the caller, hence the use of the GIL-state API).

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

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

Get the thread state that was most recently attached for this thread. (If the most recent thread state has been deleted, this returns NULL.)

If the caller has an attached thread state, it is returned.

In other terms, this function returns the thread state that will be used by PyGILState_Ensure(). If this returns NULL, then PyGILState_Ensure will create a new thread state.

This function cannot fail.

int PyGILState_Check()

Return 1 if the current thread has an attached thread state that matches the thread state returned by PyGILState_GetThisThreadState(). If the caller has no attached thread state or it otherwise doesn't match, then this returns 0.

If the current Python process has ever created a subinterpreter, this function will always return 1.

This is mainly a helper/diagnostic function.

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

منسوخ‌سازی نرم <Soft deprecated> از نسخه‌ی 3.15: Use PyThreadState_GetUnchecked() != NULL instead.

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

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

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

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

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

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

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

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

PyInterpreterState *interp

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

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

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

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

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

توجه

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

تغییر یافته در نسخه‌ی 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های سطح پایین

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 پایدار.

Return a dictionary in which extensions can store thread-specific state information. Each extension should use a unique key to store a state in the dictionary. It is okay to call this function when no thread state is attached. If this function returns NULL and no exception has been raised, then the caller should assume no thread state is attached.

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

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

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

توجه

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

تغییر یافته در نسخه‌ی 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 برای فراخوانی از مفسر اشتباه زمان‌بندی شود.

هشدار

This is a low-level function, only useful for very special cases. There is no guarantee that func will be called as quick as possible. If the main thread is busy executing a system call, func won't be called before the system call returns. This function is generally not suitable for calling Python code from arbitrary C threads. Instead, use PyThreadState_EnsureFromView().

اضافه شده در نسخه‌ی 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 پایدار.

Schedule an exception to be raised asynchronously in a thread. If the thread has a previously scheduled exception, it is overwritten.

The id argument is the thread id of the target thread, as returned by PyThread_get_thread_ident(). exc is the class of the exception to be raised, or NULL to clear the pending exception (if any).

Return the number of affected thread states. This is normally 1 if id is found, even when no change was made (the given exc was already pending, or exc is NULL but no exception is pending). If the thread id isn't found, return 0. This raises no exceptions.

To prevent naive misuse, you must write your own C extension to call this. This function must be called with an attached thread state. This function does not steal any references to exc. This function does not necessarily interrupt system calls such as sleep().

تغییر یافته در نسخه‌ی 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() and threading.Thread.ident expose this identifier to Python.

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 ندارد.