وضعیتهای نخ و قفل مفسر سراسری¶
مگر در 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.
همچنین ملاحظه نمائید
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
NULLwithout an exception set. Do not callPyThreadState_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_Ensurecall 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 correspondingPyThreadState_Release()call.اضافه شده در نسخهی 3.15.
-
void PyThreadState_Release(PyThreadStateToken *token)¶
- قسمتی از ABI پایدار از نسخهی 3.15.
Undo a
PyThreadState_Ensure()orPyThreadState_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 correspondingPyThreadState_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()در اختیار گرفته نشده بود.
-
enumerator PyGILState_LOCKED¶
-
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
SIGABRTand crash the process; seePy_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: اگر در حین نهاییسازی مفسر فراخوانی شود، نخ فعلی را معلق میکند، نه اینکه آن را خاتمه دهد.
منسوخسازی نرم <Soft deprecated> از نسخهی 3.15: Use
PyThreadState_Ensure()orPyThreadState_EnsureFromView()instead.
-
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()روی همان نخ متناظر باشد.منسوخسازی نرم <Soft deprecated> از نسخهی 3.15: Use
PyThreadState_Release()instead.
-
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 returnsNULL, thenPyGILState_Ensurewill create a new thread state.This function cannot fail.
منسوخسازی نرم <Soft deprecated> از نسخهی 3.15: Use
PyThreadState_Get()orPyThreadState_GetUnchecked()instead.
-
int PyGILState_Check()¶
Return
1if the current thread has an attached thread state that matches the thread state returned byPyGILState_GetThisThreadState(). If the caller has no attached thread state or it otherwise doesn't match, then this returns0.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() != NULLinstead.
هشدارهایی دربارهی 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¶
وضعیت مفسر این نخ.
-
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را برمیگرداند که نشان میدهد هیچ وضعیت نخ پیشینی وجود نداشته است.همچنین ملاحظه نمائید
توجه
مشابه
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
NULLand 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, orNULLto clear the pending exception (if any).Return the number of affected thread states. This is normally
1if id is found, even when no change was made (the given exc was already pending, or exc isNULLbut no exception is pending). If the thread id isn't found, return0. 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()andthreading.Thread.identexpose 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 ندارد.
همچنین ملاحظه نمائید
-
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 ندارد.