Trạng thái luồng và khóa trình thông dịch chung

Trừ khi trên free-threaded build của CPython, trình thông dịch Python thường không an toàn theo luồng. Để hỗ trợ các chương trình Python đa luồng, có một khóa chung, được gọi là global interpreter lock hoặc GIL, phải được giữ bởi một luồng trước khi truy cập các đối tượng Python. Nếu không có khóa, ngay cả những thao tác đơn giản nhất cũng có thể gây ra sự cố trong chương trình đa luồng: ví dụ: khi hai luồng đồng thời tăng số lượng tham chiếu của cùng một đối tượng, thì số lượng tham chiếu có thể chỉ tăng một lần thay vì hai lần.

Như vậy, chỉ một luồng chứa GIL mới có thể hoạt động trên các đối tượng Python hoặc gọi C API của Python.

Để mô phỏng sự tương tranh, trình thông dịch thường xuyên cố gắng chuyển đổi các luồng giữa các lệnh mã byte (xem sys.setswitchinterval()). Đây là lý do tại sao khóa cũng cần thiết để đảm bảo an toàn cho luồng trong mã Python thuần túy.

Ngoài ra, khóa trình thông dịch toàn cầu được phát hành xung quanh việc chặn các hoạt động I/O, chẳng hạn như đọc hoặc ghi vào tệp. Từ C API, việc này được thực hiện bởi detaching the thread state.

Trình thông dịch Python lưu giữ một số thông tin cục bộ theo luồng bên trong cấu trúc dữ liệu có tên là PyThreadState, còn được gọi là thread state. Mỗi luồng có một con trỏ cục bộ luồng tới PyThreadState; trạng thái luồng được tham chiếu bởi con trỏ này được coi là attached.

Một chủ đề chỉ có thể có một attached thread state tại một thời điểm. Trạng thái luồng đính kèm thường tương tự như việc giữ GIL, ngoại trừ trên các bản dựng có luồng tự do. Trên các bản dựng đã bật GIL, việc đính kèm trạng thái luồng sẽ chặn cho đến khi có thể lấy được GIL. Tuy nhiên, ngay cả trên các bản dựng bị tắt GIL, vẫn cần phải có trạng thái luồng đính kèm, vì trình thông dịch cần theo dõi xem luồng nào có thể truy cập các đối tượng Python.

Ghi chú

Ngay cả trên bản dựng có luồng tự do, việc gắn trạng thái luồng có thể bị chặn vì GIL có thể được bật lại hoặc các luồng có thể bị tạm dừng (chẳng hạn như trong quá trình thu gom rác).

Nói chung, sẽ luôn có trạng thái luồng đính kèm khi sử dụng C API của Python, bao gồm cả trong quá trình nhúng và khi triển khai các phương thức, do đó, bạn không cần phải tự mình thiết lập trạng thái luồng. Chỉ trong một số trường hợp cụ thể, chẳng hạn như trong khối Py_BEGIN_ALLOW_THREADS hoặc trong một luồng mới, luồng đó sẽ không có trạng thái luồng được đính kèm. Nếu không chắc chắn, hãy kiểm tra xem PyThreadState_GetUnchecked() có trả về NULL hay không.

Nếu hóa ra bạn cần tạo trạng thái luồng, hãy gọi PyThreadState_New() theo sau là PyThreadState_Swap() hoặc sử dụng chức năng PyGILState_Ensure() nguy hiểm.

Tách trạng thái luồng khỏi mã mở rộng

Hầu hết mã mở rộng thao tác thread state đều có cấu trúc đơn giản sau:

Lưu trạng thái luồng trong một biến cục bộ.
... Thực hiện một số thao tác chặn I/O ...
Khôi phục trạng thái luồng từ biến cục bộ.

Điều này phổ biến đến mức tồn tại một cặp macro để đơn giản hóa nó

Py_BEGIN_ALLOW_THREADS
... Thực hiện một số thao tác chặn I/O ...
Py_END_ALLOW_THREADS

Macro Py_BEGIN_ALLOW_THREADS mở một khối mới và khai báo một biến cục bộ ẩn; macro Py_END_ALLOW_THREADS đóng khối.

Khối ở trên mở rộng thành đoạn mã sau:

PyThreadState *_save;

_save = PyEval_SaveThread();
... Thực hiện một số thao tác chặn I/O ...
PyEval_RestoreThread(_save);

Đây là cách các chức năng này hoạt động:

Trạng thái luồng đính kèm ngụ ý rằng GIL được giữ cho trình thông dịch. Để tách nó ra, PyEval_SaveThread() được gọi và kết quả được lưu trữ trong một biến cục bộ.

Bằng cách tách trạng thái luồng, GIL được giải phóng, cho phép các luồng khác gắn vào trình thông dịch và thực thi trong khi luồng hiện tại thực hiện chặn I/O. Khi thao tác I/O hoàn tất, trạng thái luồng cũ được gắn lại bằng cách gọi PyEval_RestoreThread(), thao tác này sẽ đợi cho đến khi có thể lấy được GIL.

Ghi chú

Thực hiện chặn I/O là trường hợp sử dụng phổ biến nhất để tách trạng thái luồng, nhưng cũng hữu ích khi gọi nó qua mã gốc chạy dài không cần quyền truy cập vào đối tượng Python hoặc C API của Python. Ví dụ: mô-đun zlibhashlib tiêu chuẩn sẽ tách thread state khi nén hoặc băm dữ liệu.

Trên free-threaded build, GIL thường không được áp dụng, nhưng detaching the thread state is still required, vì trình thông dịch định kỳ cần chặn tất cả các luồng để có được cái nhìn nhất quán về các đối tượng Python mà không gặp rủi ro về điều kiện chủng tộc. Ví dụ: CPython hiện tạm dừng tất cả các luồng trong một khoảng thời gian ngắn khi chạy trình thu gom rác.

Cảnh báo

Việc tách trạng thái luồng có thể dẫn đến hành vi không mong muốn trong quá trình hoàn thiện trình thông dịch. Xem Cảnh báo về việc hoàn tất thời gian chạy để biết thêm chi tiết.

API

Các macro sau đây thường được sử dụng không có dấu chấm phẩy ở cuối; tìm cách sử dụng ví dụ trong bản phân phối nguồn Python.

Ghi chú

Những macro này vẫn cần thiết trên free-threaded build để ngăn chặn tình trạng bế tắc.

Py_BEGIN_ALLOW_THREADS
Một phần của ABI ổn định.

Macro này mở rộng tới { PyThreadState *_save; _save = PyEval_SaveThread();. Lưu ý rằng nó có chứa một dấu ngoặc mở; nó phải được khớp với macro Py_END_ALLOW_THREADS sau. Xem ở trên để thảo luận thêm về macro này.

Py_END_ALLOW_THREADS
Một phần của ABI ổn định.

Macro này mở rộng tới PyEval_RestoreThread(_save); }. Lưu ý rằng nó có chứa dấu ngoặc nhọn đóng; nó phải được khớp với macro Py_BEGIN_ALLOW_THREADS trước đó. Xem ở trên để thảo luận thêm về macro này.

Py_BLOCK_THREADS
Một phần của ABI ổn định.

Macro này mở rộng thành PyEval_RestoreThread(_save);: nó tương đương với Py_END_ALLOW_THREADS mà không có dấu ngoặc nhọn đóng.

Py_UNBLOCK_THREADS
Một phần của ABI ổn định.

Macro này mở rộng thành _save = PyEval_SaveThread();: nó tương đương với Py_BEGIN_ALLOW_THREADS mà không có dấu ngoặc nhọn mở và khai báo biến.

Chủ đề được tạo không phải bằng Python

Khi các luồng được tạo bằng API Python chuyên dụng (chẳng hạn như mô-đun threading), trạng thái luồng sẽ tự động được liên kết với chúng. Tuy nhiên, khi một luồng được tạo từ mã gốc (ví dụ: bởi thư viện bên thứ ba có quản lý luồng riêng), thì luồng đó không giữ trạng thái luồng đính kèm.

Nếu bạn cần gọi mã Python từ các luồng này (thường thì đây sẽ là một phần của lệnh gọi lại API do thư viện bên thứ ba nói trên cung cấp), trước tiên bạn phải đăng ký các luồng này với trình thông dịch bằng cách tạo trạng thái luồng mới và đính kèm nó.

Cách mạnh mẽ nhất để thực hiện việc này là thông qua PyThreadState_New(), sau đó là PyThreadState_Swap().

Ghi chú

PyThreadState_New yêu cầu một đối số trỏ đến trình thông dịch mong muốn; một con trỏ như vậy có thể được lấy thông qua lệnh gọi tới PyInterpreterState_Get() từ mã nơi chuỗi được tạo.

Ví dụ:

/* Giá trị trả về của PyInterpreterState_Get() từ
   chức năng đã tạo ra chủ đề này. */
PyInterpreterState *interp = thread_data->interp;

/* Tạo trạng thái luồng mới cho trình thông dịch. Nó không bắt đầu
   đính kèm. */
PyThreadState *tstate = PyThreadState_New(interp);

/* Đính kèm trạng thái luồng, trạng thái này sẽ thu được GIL. */
PyThreadState_Swap(tstate);

/* Thực hiện các thao tác Python tại đây. */
result = CallSomeFunction();
/*đánh giá kết quả hoặc xử lý ngoại lệ */

/* Phá hủy trạng thái của luồng. Không có Python API nào được phép vượt quá thời điểm này. */
PyThreadState_Clear(tstate);
PyThreadState_DeleteCurrent();

Cảnh báo

Nếu trình thông dịch hoàn tất trước khi PyThreadState_Swap được gọi thì interp sẽ là một con trỏ lơ lửng!

API kế thừa

Một mẫu phổ biến khác để gọi mã Python từ một luồng không phải Python là sử dụng PyGILState_Ensure(), sau đó gọi tới PyGILState_Release().

Các hàm này không hoạt động tốt khi có nhiều trình thông dịch trong quy trình Python. Nếu chưa có trình thông dịch Python nào được sử dụng trong luồng hiện tại (điều này thường xảy ra với các luồng được tạo bên ngoài Python), PyGILState_Ensure sẽ tạo và đính kèm trạng thái luồng cho trình thông dịch "chính" (trình thông dịch đầu tiên trong quy trình Python).

Ngoài ra, các chức năng này có vấn đề về an toàn luồng trong quá trình hoàn thiện trình thông dịch. Việc sử dụng PyGILState_Ensure trong quá trình hoàn thiện có thể sẽ làm hỏng quá trình.

Cách sử dụng các chức năng này trông giống như sau:

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

/* Thực hiện các thao tác Python tại đây. */
result = CallSomeFunction();
/*đánh giá kết quả hoặc xử lý ngoại lệ */

/* Giải phóng luồng. Không có Python API nào được phép vượt quá thời điểm này. */
PyGILState_Release(gstate);

Cảnh báo về fork()

Một điều quan trọng khác cần lưu ý về các luồng là hành vi của chúng khi đối mặt với lệnh gọi C fork(). Trên hầu hết các hệ thống có fork(), sau một quá trình phân nhánh, chỉ có luồng phát hành phân nhánh mới tồn tại. Điều này có tác động cụ thể đến cả cách xử lý khóa và đến tất cả trạng thái được lưu trữ trong thời gian chạy của CPython.

Thực tế là chỉ còn lại luồng "hiện tại" có nghĩa là mọi khóa được giữ bởi các luồng khác sẽ không bao giờ được giải phóng. Python giải quyết vấn đề này cho os.fork() bằng cách lấy các khóa mà nó sử dụng bên trong trước fork và giải phóng chúng sau đó. Ngoài ra, nó còn đặt lại bất kỳ Khóa đối tượng nào ở trẻ em. Khi mở rộng hoặc nhúng Python, không có cách nào để thông báo cho Python về các khóa bổ sung (không phải Python) cần được lấy trước hoặc đặt lại sau một nhánh. Các cơ sở hệ điều hành như pthread_atfork() sẽ cần được sử dụng để thực hiện điều tương tự. Ngoài ra, khi mở rộng hoặc nhúng Python, việc gọi trực tiếp fork() thay vì thông qua os.fork() (và quay lại hoặc gọi vào Python) có thể dẫn đến bế tắc do một trong các khóa bên trong của Python bị giữ bởi một luồng không còn tồn tại sau ngã ba. PyOS_AfterFork_Child() cố gắng đặt lại các khóa cần thiết nhưng không phải lúc nào cũng làm được.

Việc tất cả các luồng khác biến mất cũng có nghĩa là trạng thái thời gian chạy của CPython ở đó phải được dọn dẹp đúng cách, điều mà os.fork() thực hiện. Điều này có nghĩa là hoàn thiện tất cả các đối tượng PyThreadState khác thuộc về trình thông dịch hiện tại và tất cả các đối tượng PyInterpreterState khác. Do điều này và tính chất đặc biệt của "main" interpreter, fork() chỉ nên được gọi trong chuỗi "chính" của trình thông dịch đó, nơi thời gian chạy toàn cầu CPython ban đầu được khởi tạo. Ngoại lệ duy nhất là nếu exec() sẽ được gọi ngay sau đó.

API cấp cao

Đây là những loại và hàm được sử dụng phổ biến nhất khi viết phần mở rộng C đa luồng.

type PyThreadState
Một phần của API có giới hạn (như một cấu trúc mờ đục).

Cấu trúc dữ liệu này biểu thị trạng thái của một luồng. Thành viên dữ liệu công khai duy nhất là:

PyInterpreterState *interp

Trạng thái thông dịch viên của chủ đề này.

void PyEval_InitThreads()
Một phần của ABI ổn định.

Chức năng không được dùng nữa mà không làm gì cả.

Trong Python 3.6 trở lên, hàm này đã tạo GIL nếu nó không tồn tại.

Thay đổi trong phiên bản 3.9: Chức năng bây giờ không làm gì cả.

Thay đổi trong phiên bản 3.7: Hàm này hiện được Py_Initialize() gọi nên bạn không cần phải tự gọi nó nữa.

Thay đổi trong phiên bản 3.2: Chức năng này không thể được gọi trước Py_Initialize() nữa.

Sắp loại bỏ từ phiên bản 3.9.

PyThreadState *PyEval_SaveThread()
Một phần của ABI ổn định.

Tháo attached thread state ra và trả lại. Chủ đề sẽ không có thread state khi quay trở lại.

void PyEval_RestoreThread(PyThreadState *tstate)
Một phần của ABI ổn định.

Đặt attached thread state thành tstate. thread state should not đã vượt qua là attached, nếu không sẽ xảy ra bế tắc. tstate sẽ được đính kèm khi quay trở lại.

Ghi chú

Việc gọi hàm này từ một luồng khi thời gian chạy đang hoàn tất sẽ treo luồng đó cho đến khi chương trình thoát ra, ngay cả khi luồng đó không được Python tạo ra. Tham khảo Cảnh báo về việc hoàn tất thời gian chạy để biết thêm chi tiết.

Thay đổi trong phiên bản 3.14: Treo chuỗi hiện tại, thay vì chấm dứt nó, nếu được gọi trong khi trình thông dịch đang hoàn thiện.

PyThreadState *PyThreadState_Get()
Một phần của ABI ổn định.

Trả lại attached thread state. Nếu luồng không có trạng thái luồng đính kèm, (chẳng hạn như khi ở bên trong khối Py_BEGIN_ALLOW_THREADS), thì điều này sẽ gây ra lỗi nghiêm trọng (do đó người gọi không cần kiểm tra NULL).

Xem thêm PyThreadState_GetUnchecked().

PyThreadState *PyThreadState_GetUnchecked()

Tương tự như PyThreadState_Get(), nhưng đừng hủy tiến trình với một lỗi nghiêm trọng nếu đó là NULL. Người gọi có trách nhiệm kiểm tra xem kết quả có phải là NULL hay không.

Added in version 3.13: Trong Python 3.5 đến 3.12, hàm này ở chế độ riêng tư và được gọi là _PyThreadState_UncheckedGet().

PyThreadState *PyThreadState_Swap(PyThreadState *tstate)
Một phần của ABI ổn định.

Đặt attached thread state thành tstate và trả lại thread state đã được đính kèm trước khi gọi.

Chức năng này có thể gọi an toàn mà không cần attached thread state; nó sẽ chỉ trả về NULL cho biết rằng không có trạng thái luồng trước đó.

Ghi chú

Tương tự như PyGILState_Ensure(), chức năng này sẽ treo luồng nếu quá trình chạy kết thúc.

API trạng thái GIL

Các hàm sau sử dụng bộ lưu trữ cục bộ theo luồng và không tương thích với trình thông dịch phụ:

type PyGILState_STATE
Một phần của ABI ổn định.

Loại giá trị được PyGILState_Ensure() trả về và được chuyển tới PyGILState_Release().

enumerator PyGILState_LOCKED

GIL đã được giữ khi PyGILState_Ensure() được gọi.

enumerator PyGILState_UNLOCKED

GIL không được giữ khi PyGILState_Ensure() được gọi.

PyGILState_STATE PyGILState_Ensure()
Một phần của ABI ổn định.

Đảm bảo rằng luồng hiện tại sẵn sàng gọi Python C API bất kể trạng thái hiện tại của Python hay attached thread state. Điều này có thể được một luồng gọi bao nhiêu lần tùy thích miễn là mỗi lệnh gọi được khớp với lệnh gọi tới PyGILState_Release(). Nói chung, các API liên quan đến luồng khác có thể được sử dụng giữa các lệnh gọi PyGILState_Ensure()PyGILState_Release() miễn là trạng thái luồng được khôi phục về trạng thái trước đó trước Release(). Ví dụ: việc sử dụng macro Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS bình thường là có thể chấp nhận được.

Giá trị trả về là một "điều khiển" mờ đối với attached thread state khi PyGILState_Ensure() được gọi và phải được chuyển tới PyGILState_Release() để đảm bảo Python được giữ nguyên trạng thái. Mặc dù các cuộc gọi đệ quy được cho phép, nhưng các bộ điều khiển cannot này vẫn được chia sẻ - mỗi lệnh gọi duy nhất tới PyGILState_Ensure() phải lưu bộ điều khiển cho lệnh gọi tới PyGILState_Release() của nó.

Khi hàm trả về, sẽ có attached thread state và luồng sẽ có thể gọi mã Python tùy ý. Thất bại là một lỗi nghiêm trọng.

Cảnh báo

Gọi hàm này khi thời gian chạy đang hoàn tất là không an toàn. Làm như vậy sẽ treo luồng cho đến khi chương trình kết thúc hoặc làm hỏng hoàn toàn trình thông dịch trong một số trường hợp hiếm gặp. Tham khảo Cảnh báo về việc hoàn tất thời gian chạy để biết thêm chi tiết.

Thay đổi trong phiên bản 3.14: Treo chuỗi hiện tại, thay vì chấm dứt nó, nếu được gọi trong khi trình thông dịch đang hoàn thiện.

void PyGILState_Release(PyGILState_STATE)
Một phần của ABI ổn định.

Giải phóng mọi tài nguyên có được trước đó. Sau cuộc gọi này, trạng thái của Python sẽ giống như trước cuộc gọi PyGILState_Ensure() tương ứng (nhưng nhìn chung người gọi sẽ không biết trạng thái này, do đó việc sử dụng GILState API).

Mọi lệnh gọi tới PyGILState_Ensure() phải được khớp với lệnh gọi đến PyGILState_Release() trên cùng một chuỗi.

PyThreadState *PyGILState_GetThisThreadState()
Một phần của ABI ổn định.

Nhận attached thread state cho chủ đề này. Có thể trả về NULL nếu không có GILState API nào được sử dụng trên chuỗi hiện tại. Lưu ý rằng luồng chính luôn có trạng thái luồng như vậy, ngay cả khi không có lệnh gọi trạng thái luồng tự động nào được thực hiện trên luồng chính. Đây chủ yếu là chức năng trợ giúp/chẩn đoán.

Ghi chú

Hàm này có thể trả về non-NULL ngay cả khi thread state được tách ra. Ưu tiên PyThreadState_Get() hoặc PyThreadState_GetUnchecked() trong hầu hết các trường hợp.

int PyGILState_Check()

Trả về 1 nếu luồng hiện tại đang giữ GIL0 nếu ngược lại. Hàm này có thể được gọi từ bất kỳ luồng nào vào bất kỳ lúc nào. Chỉ khi nó đã khởi tạo thread state thông qua PyGILState_Ensure() thì nó mới trả về 1. Đây chủ yếu là chức năng trợ giúp/chẩn đoán. Ví dụ, nó có thể hữu ích trong bối cảnh gọi lại hoặc chức năng cấp phát bộ nhớ khi biết rằng GIL bị khóa có thể cho phép người gọi thực hiện các hành động nhạy cảm hoặc hành xử khác đi.

Ghi chú

Nếu quy trình Python hiện tại đã từng tạo một trình thông dịch con, hàm này sẽ always trả về 1. Thích PyThreadState_GetUnchecked() cho hầu hết các trường hợp.

Added in version 3.4.

API cấp thấp

PyThreadState *PyThreadState_New(PyInterpreterState *interp)
Một phần của ABI ổn định.

Tạo một đối tượng trạng thái luồng mới thuộc đối tượng trình thông dịch đã cho. Không cần attached thread state.

void PyThreadState_Clear(PyThreadState *tstate)
Một phần của ABI ổn định.

Đặt lại tất cả thông tin trong đối tượng thread state. tstate chắc chắn là attached

Thay đổi trong phiên bản 3.9: Hàm này bây giờ gọi lại lệnh gọi lại PyThreadState.on_delete. Trước đây, điều đó đã xảy ra ở PyThreadState_Delete().

Thay đổi trong phiên bản 3.13: Lệnh gọi lại PyThreadState.on_delete đã bị xóa.

void PyThreadState_Delete(PyThreadState *tstate)
Một phần của ABI ổn định.

Phá hủy một đối tượng thread state. tstate không được là attached đối với bất kỳ chủ đề nào. tstate phải được đặt lại bằng lệnh gọi tới PyThreadState_Clear() trước đó.

void PyThreadState_DeleteCurrent(void)

Tách attached thread state (phải được đặt lại bằng lệnh gọi PyThreadState_Clear() trước đó) rồi hủy nó.

Không có thread state sẽ là attached khi quay trở lại.

PyFrameObject *PyThreadState_GetFrame(PyThreadState *tstate)
Một phần của ABI ổn định kể từ phiên bản 3.10.

Lấy khung hiện tại của trạng thái luồng Python tstate.

Trả về strong reference. Trả về NULL nếu hiện tại không có khung nào đang thực thi.

Xem thêm PyEval_GetFrame().

tstate không được là NULL mà phải là attached.

Added in version 3.9.

uint64_t PyThreadState_GetID(PyThreadState *tstate)
Một phần của ABI ổn định kể từ phiên bản 3.10.

Nhận mã định danh thread state duy nhất của trạng thái luồng Python tstate.

tstate không được là NULL mà phải là attached.

Added in version 3.9.

PyInterpreterState *PyThreadState_GetInterpreter(PyThreadState *tstate)
Một phần của ABI ổn định kể từ phiên bản 3.10.

Nhận trình thông dịch của trạng thái luồng Python tstate.

tstate không được là NULL mà phải là attached.

Added in version 3.9.

void PyThreadState_EnterTracing(PyThreadState *tstate)

Tạm dừng theo dõi và lập hồ sơ ở trạng thái luồng Python tstate.

Tiếp tục chúng bằng chức năng PyThreadState_LeaveTracing().

Added in version 3.11.

void PyThreadState_LeaveTracing(PyThreadState *tstate)

Tiếp tục theo dõi và lập hồ sơ ở trạng thái luồng Python tstate bị đình chỉ bởi hàm PyThreadState_EnterTracing().

Xem thêm các hàm PyEval_SetTrace()PyEval_SetProfile().

Added in version 3.11.

int PyUnstable_ThreadState_SetStackProtection(PyThreadState *tstate, void *stack_start_addr, size_t stack_size)
Đây là API không ổn định. Nó có thể thay đổi mà không có cảnh báo trong các bản phát hành nhỏ.

Đặt địa chỉ bắt đầu bảo vệ ngăn xếp và kích thước bảo vệ ngăn xếp của trạng thái luồng Python.

Nếu thành công, hãy trả về 0. Nếu thất bại, hãy đặt ngoại lệ và trả về -1.

CPython triển khai recursion control cho mã C bằng cách tăng RecursionError khi nhận thấy ngăn xếp thực thi của máy gần tràn. Xem ví dụ hàm Py_EnterRecursiveCall(). Để làm được điều này, nó cần biết vị trí ngăn xếp của luồng hiện tại mà nó thường lấy từ hệ điều hành. Khi ngăn xếp bị thay đổi, ví dụ như sử dụng các kỹ thuật chuyển ngữ cảnh như boost::context của thư viện Boost, bạn phải gọi PyUnstable_ThreadState_SetStackProtection() để thông báo cho CPython về thay đổi.

Gọi PyUnstable_ThreadState_SetStackProtection() trước hoặc sau khi thay đổi ngăn xếp. Không gọi bất kỳ Python C API nào khác giữa cuộc gọi và thay đổi ngăn xếp.

Xem PyUnstable_ThreadState_ResetStackProtection() để hoàn tác thao tác này.

Added in version 3.15.

void PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate)
Đây là API không ổn định. Nó có thể thay đổi mà không có cảnh báo trong các bản phát hành nhỏ.

Đặt lại địa chỉ bắt đầu bảo vệ ngăn xếp và kích thước bảo vệ ngăn xếp của trạng thái luồng Python về mặc định của hệ điều hành.

Xem PyUnstable_ThreadState_SetStackProtection() để được giải thích.

Added in version 3.15.

PyObject *PyThreadState_GetDict()
Giá trị trả về: Tham chiếu mượn. Một phần của ABI ổn định.

Trả về một từ điển trong đó các tiện ích mở rộng có thể lưu trữ thông tin trạng thái cụ thể của luồng. Mỗi tiện ích mở rộng nên sử dụng một khóa duy nhất để sử dụng để lưu trữ trạng thái trong từ điển. Gọi hàm này khi không có thread stateattached cũng được. Nếu hàm này trả về NULL thì không có ngoại lệ nào được đưa ra và người gọi sẽ cho rằng không có trạng thái luồng nào được đính kèm.

void PyEval_AcquireThread(PyThreadState *tstate)
Một phần của ABI ổn định.

Attach tstate vào chuỗi hiện tại, không được là NULL hoặc đã là attached.

Chuỗi cuộc gọi không được có attached thread state.

Ghi chú

Việc gọi hàm này từ một luồng khi thời gian chạy đang hoàn tất sẽ treo luồng đó cho đến khi chương trình thoát ra, ngay cả khi luồng đó không được Python tạo ra. Tham khảo Cảnh báo về việc hoàn tất thời gian chạy để biết thêm chi tiết.

Thay đổi trong phiên bản 3.8: Đã cập nhật để nhất quán với PyEval_RestoreThread(), Py_END_ALLOW_THREADS()PyGILState_Ensure(), đồng thời chấm dứt chuỗi hiện tại nếu được gọi trong khi trình thông dịch đang hoàn tất.

Thay đổi trong phiên bản 3.14: Treo chuỗi hiện tại, thay vì chấm dứt nó, nếu được gọi trong khi trình thông dịch đang hoàn thiện.

PyEval_RestoreThread() là một hàm cấp cao hơn luôn khả dụng (ngay cả khi các luồng chưa được khởi tạo).

void PyEval_ReleaseThread(PyThreadState *tstate)
Một phần của ABI ổn định.

Tháo attached thread state ra. Đối số tstate, không được là NULL, chỉ được sử dụng để kiểm tra xem nó có đại diện cho attached thread state --- nếu không, sẽ báo cáo một lỗi nghiêm trọng.

PyEval_SaveThread() là một hàm cấp cao hơn luôn khả dụng (ngay cả khi các luồng chưa được khởi tạo).

Thông báo không đồng bộ

Một cơ chế được cung cấp để tạo thông báo không đồng bộ cho luồng trình thông dịch chính. Các thông báo này có dạng con trỏ hàm và đối số con trỏ void.

int Py_AddPendingCall(int (*func)(void*), void *arg)
Một phần của ABI ổn định.

Lên lịch cho một chức năng được gọi từ chuỗi trình thông dịch chính. Khi thành công, 0 được trả về và func được xếp hàng đợi để được gọi trong luồng chính. Khi thất bại, -1 được trả về mà không đặt bất kỳ ngoại lệ nào.

Khi được xếp hàng thành công, func sẽ được gọi eventually từ chuỗi trình thông dịch chính với đối số arg. Nó sẽ được gọi không đồng bộ đối với mã Python đang chạy thông thường, nhưng đáp ứng cả hai điều kiện sau:

func phải trả về 0 nếu thành công hoặc -1 nếu thất bại với một bộ ngoại lệ. func sẽ không bị gián đoạn để thực hiện đệ quy một thông báo không đồng bộ khác, nhưng nó vẫn có thể bị gián đoạn để chuyển luồng nếu thread state bị tách ra.

Chức năng này không cần attached thread state. Tuy nhiên, để gọi hàm này trong trình thông dịch phụ, trình gọi phải có attached thread state. Nếu không, hàm func có thể được lên lịch để gọi từ trình thông dịch sai.

Cảnh báo

Đây là chức năng cấp thấp, chỉ hữu ích cho những trường hợp rất đặc biệt. Không có gì đảm bảo rằng func sẽ được gọi nhanh nhất có thể. Nếu luồng chính đang bận thực hiện lệnh gọi hệ thống, func sẽ không được gọi trước khi lệnh gọi hệ thống quay trở lại. Hàm này nói chung là not phù hợp để gọi mã Python từ các luồng C tùy ý. Thay vào đó, hãy sử dụng PyGILState API.

Added in version 3.1.

Thay đổi trong phiên bản 3.9: Nếu hàm này được gọi trong trình thông dịch phụ thì hàm func hiện được lên lịch để gọi từ trình thông dịch phụ, thay vì được gọi từ trình thông dịch chính. Mỗi thông dịch viên phụ hiện có danh sách các cuộc gọi được lên lịch riêng.

Thay đổi trong phiên bản 3.12: Chức năng này hiện luôn lên lịch chạy func trong trình thông dịch chính.

int Py_MakePendingCalls(void)
Một phần của ABI ổn định.

Thực hiện tất cả các cuộc gọi đang chờ xử lý. Điều này thường được thực hiện tự động bởi trình thông dịch.

Hàm này trả về 0 nếu thành công và trả về -1 với ngoại lệ được đặt nếu thất bại.

Nếu điều này không được gọi trong luồng chính của trình thông dịch chính thì hàm này sẽ không thực hiện gì và trả về 0. Người gọi phải giữ attached thread state.

Added in version 3.1.

Thay đổi trong phiên bản 3.12: Chức năng này chỉ chạy các cuộc gọi đang chờ xử lý trong trình thông dịch chính.

int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)
Một phần của ABI ổn định.

Đưa ra một ngoại lệ không đồng bộ trong một chuỗi. Đối số id là id luồng của luồng đích; exc là đối tượng ngoại lệ được nêu ra. Hàm này không lấy cắp bất kỳ tham chiếu nào tới exc. Để tránh việc lạm dụng một cách ngây thơ, bạn phải viết phần mở rộng C của riêng mình để gọi nó. Phải được gọi bằng attached thread state. Trả về số trạng thái luồng đã sửa đổi; đây thường là một, nhưng sẽ bằng 0 nếu không tìm thấy id luồng. Nếu excNULL, ngoại lệ đang chờ xử lý (nếu có) cho chuỗi sẽ bị xóa. Điều này không có ngoại lệ.

Thay đổi trong phiên bản 3.7: Loại tham số id đã thay đổi từ long thành unsigned long.

API luồng của hệ điều hành

PYTHREAD_INVALID_THREAD_ID

Giá trị Sentinel cho ID luồng không hợp lệ.

Điều này hiện tương đương với (unsigned long)-1.

unsigned long PyThread_start_new_thread(void (*func)(void*), void *arg)
Một phần của ABI ổn định.

Bắt đầu hàm func trong một chuỗi mới với đối số arg. Chủ đề kết quả không có ý định được tham gia.

func không được là NULL, nhưng arg có thể là NULL.

Nếu thành công, hàm này trả về mã định danh của luồng mới; nếu thất bại, điều này sẽ trả về PYTHREAD_INVALID_THREAD_ID.

Người gọi không cần giữ attached thread state.

unsigned long PyThread_get_thread_ident(void)
Một phần của ABI ổn định.

Trả về mã định danh của luồng hiện tại, giá trị này sẽ không bao giờ bằng 0.

Chức năng này không thể bị lỗi và người gọi không cần giữ attached thread state.

PyObject *PyThread_GetInfo(void)
Một phần của ABI ổn định kể từ phiên bản 3.3.

Nhận thông tin chung về luồng hiện tại ở dạng đối tượng struct sequence. Thông tin này có thể truy cập được dưới dạng sys.thread_info trong Python.

Nếu thành công, thao tác này sẽ trả về một strong reference mới cho thông tin luồng; nếu thất bại, điều này sẽ trả về NULL với một bộ ngoại lệ.

Người gọi phải giữ attached thread state.

PY_HAVE_THREAD_NATIVE_ID

Macro này được xác định khi hệ thống hỗ trợ ID luồng gốc.

unsigned long PyThread_get_thread_native_id(void)
Một phần của ABI ổn định on platforms with native thread IDs.

Lấy mã định danh riêng của luồng hiện tại do nó được gán bởi nhân của hệ điều hành, giá trị này sẽ không bao giờ nhỏ hơn 0.

Chức năng này chỉ khả dụng khi PY_HAVE_THREAD_NATIVE_ID được xác định.

Chức năng này không thể bị lỗi và người gọi không cần giữ attached thread state.

void PyThread_exit_thread(void)
Một phần của ABI ổn định.

Chấm dứt chủ đề hiện tại. Chức năng này thường được coi là không an toàn và nên tránh. Nó được giữ chỉ để tương thích ngược.

Chức năng này chỉ an toàn để gọi nếu tất cả các chức năng trong ngăn xếp cuộc gọi đầy đủ được ghi để cho phép nó một cách an toàn.

Cảnh báo

Nếu hệ thống hiện tại sử dụng các luồng POSIX (còn được gọi là "pthread"), thì lệnh này gọi pthread_exit(3), cố gắng giải phóng ngăn xếp và gọi hàm hủy C++ trên một số triển khai libc. Tuy nhiên, nếu đạt được chức năng noexcept, nó có thể chấm dứt quá trình. Các hệ thống khác, chẳng hạn như macOS, thực hiện việc giải nén.

Trên Windows, hàm này gọi _endthreadex(), chức năng này sẽ tắt luồng mà không gọi hàm hủy C++.

Trong mọi trường hợp, có nguy cơ hỏng ngăn xếp của luồng.

Sắp loại bỏ từ phiên bản 3.14.

void PyThread_init_thread(void)
Một phần của ABI ổn định.

Khởi tạo API PyThread*. Python thực thi chức năng này một cách tự động, do đó không cần phải gọi nó từ mô-đun mở rộng.

int PyThread_set_stacksize(size_t size)
Một phần của ABI ổn định.

Đặt kích thước ngăn xếp của luồng hiện tại thành byte size.

Hàm này trả về 0 nếu thành công, -1 nếu size không hợp lệ hoặc -2 nếu hệ thống không hỗ trợ thay đổi kích thước ngăn xếp. Chức năng này không đặt ngoại lệ.

Người gọi không cần giữ attached thread state.

size_t PyThread_get_stacksize(void)
Một phần của ABI ổn định.

Trả về kích thước ngăn xếp của luồng hiện tại theo byte hoặc 0 nếu kích thước ngăn xếp mặc định của hệ thống đang được sử dụng.

Người gọi không cần giữ attached thread state.