原文:http://blog.csdn.net/marising/article/details/3032278
Python官方文檔鏈接如下:
http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock
本人在網上翻譯版本的基礎上修改了。
The Python interpreter is not fully thread safe. In order to support multi-threaded Python programs, there's a global lock that must be held by the current thread before it can safely access Python objects. Without the lock, even the simplest operations could cause problems in a multi-threaded program: for example, when two threads simultaneously increment the reference count of the same object, the reference count could end up being incremented only once instead of twice.
Python 解釋器不是完全線程安全的。為了支持多線程Python程序,當前線程在安全訪問Python對象之前,必須持有全局鎖(GIL,Global Interpreter Lock,全局解釋器鎖)。如果沒有鎖,多線程中甚至最簡單的操作都會發生問題。例如,兩個線程同時增加一個對象的引用計數,該引用計數可能只增加了一次而非兩次。
Therefore, the rule exists that only the thread that has acquired the global interpreter lock may operate on Python objects or call Python/C API functions. In order to support multi-threaded Python programs, the interpreter regularly releases and reacquires the lock -- by default, every 100 bytecode instructions (this can be changed with sys.setcheckinterval()). The lock is also released and reacquired around potentially blocking I/O operations like reading or writing a file, so that other threads can run while the thread that requests the I/O is waiting for the I/O operation to complete.
因此,存在一個規則:只有獲得了全局解釋器鎖的線程才能操作 Python 對象或者調用Python/C API函數。為了支持多線程 Python 編程,解釋器有規律的釋放和回收鎖——默認情況下,每100字節指令集循環一次(可以通過sys.setcheckinterval()設置)。類似文件讀寫之類的 i/o 片也會隨鎖釋放和回收,這樣其它的線程在請求 I/O 操作的線程等待I/O操作完成的時候也可以運行。
The Python interpreter needs to keep some bookkeeping information separate per thread -- for this it uses a data structure called PyThreadState. There's one global variable, however: the pointer to the current PyThreadState structure. While most thread packages have a way to store "per-thread global data", Python's internal platform independent thread abstraction doesn't support this yet. Therefore, the current thread state must be manipulated explicitly.
Python解釋器需要為每個獨立的線程保留一些薄記信息——為此它使用一個稱為PyThreadState的數據結構。然而,這是一個全局變量:當前 PyThreadState 結構的指針。盡管大多數線程包都有辦法保存“每線程全局數據”,Python 的內置平台無關線程指令還不支持它。因此,必須明確操作當前的線程狀態。
This is easy enough in most cases. Most code manipulating the global interpreter lock has the following simple structure:
大多數境況下這都是很簡單的。全局解釋器鎖的操作代碼主要是以下結構:
- Save the thread state in a local variable.
- Release the interpreter lock.
- ...Do some blocking I/O operation...
- Reacquire the interpreter lock.
- Restore the thread state from the local variable.
This is so common that a pair of macros exists to simplify it:
這種方式如此通用,我們可以用一對現成的宏來簡化它:
- Py_BEGIN_ALLOW_THREADS
- ...Do some blocking I/O operation...
- Py_END_ALLOW_THREADS
The Py_BEGIN_ALLOW_THREADS macro opens a new block and declares a hidden local variable; the Py_END_ALLOW_THREADS macro closes the block. Another advantage of using these two macros is that when Python is compiled without thread support, they are defined empty, thus saving the thread state and lock manipulations.
Py_BEGIN_ALLOW_THREADS 宏打開一個新的 block 並且定義一個隱藏的局部變量;Py_END_ALLOW_THREADS 宏關閉這個 block 。這兩個宏還有一個高級的用途:如果 Python 編譯為不支持線程的版本,他們定義為空,因此保存線程狀態並鎖定操作。
When thread support is enabled, the block above expands to the following code:
如果支持線程,這個 block 就會展開為以下代碼:
- PyThreadState *_save;
- _save = PyEval_SaveThread();
- //...Do some blocking I/O operation...
- PyEval_RestoreThread(_save);
Using even lower level primitives, we can get roughly the same effect as follows:
使用更低級的元素,我們可以獲得同樣的效果:
- PyThreadState *_save;
- _save = PyThreadState_Swap(NULL);
- PyEval_ReleaseLock();
- //...Do some blocking I/O operation...
- PyEval_AcquireLock();
- PyThreadState_Swap(_save);
There are some subtle differences; in particular, PyEval_RestoreThread() saves and restores the value of the global variable errno, since the lock manipulation does not guarantee that errno is left alone. Also, when thread support is disabled, PyEval_SaveThread() and PyEval_RestoreThread() don't manipulate the lock; in this case, PyEval_ReleaseLock() and PyEval_AcquireLock() are not available. This is done so that dynamically loaded extensions compiled with thread support enabled can be loaded by an interpreter that was compiled with disabled thread support.
這里有些微妙的不同,細節上,因為鎖操作不保證全局變量 erron 的一致,PyEval_RestoreThread() 保存和恢復 errno。同樣,不支持線程時,PyEval_SaveThread() 和 PyEval_RestoreThread() 不操作鎖,在這種情況下 PyEval_ReleaseLock() 和 PyEval_AcquireLock() 不可用。這使得不支持線程的解釋器可以動態加載支持線程的擴展。
The global interpreter lock is used to protect the pointer to the current thread state. When releasing the lock and saving the thread state, the current thread state pointer must be retrieved before the lock is released (since another thread could immediately acquire the lock and store its own thread state in the global variable). Conversely, when acquiring the lock and restoring the thread state, the lock must be acquired before storing the thread state pointer.
全局解釋器鎖用於保護當前線程狀態的指針。當釋放鎖並保存狀態的時候,當前線程狀態指針必須在鎖釋放之前回收(因為另一個指針將會隨之獲取鎖並且在全局變量中保存它自己的線程狀態)。相反,獲取鎖並恢復線程狀態的時候,鎖必須在保存狀態指針之前就獲得。
Why am I going on with so much detail about this? Because when threads are created from C, they don't have the global interpreter lock, nor is there a thread state data structure for them. Such threads must bootstrap themselves into existence, by first creating a thread state data structure, then acquiring the lock, and finally storing their thread state pointer, before they can start using the Python/C API. When they are done, they should reset the thread state pointer, release the lock, and finally free their thread state data structure.
為什么我要對這些進行詳細介紹?因為從 C 中創建線程的時候,它們沒有全局解釋器鎖,也沒有對應的線程狀態數據結構。這些線程在他們使用 Python/C API 之前必須自舉,首先要創建線程狀態數據結構,然后獲取鎖,最后保存它們的線程狀態指針。完成工作之后,他們可以重置線程狀態指針,釋放鎖,最后釋放他們的線程數據結構。
Beginning with version 2.3, threads can now take advantage of the PyGILState_*() functions to do all of the above automatically. The typical idiom for calling into Python from a C thread is now:
自2.3版開始,線程可以使用 PyGILState_*()函數方便的自動獲取以上的所有功能。從C線程中進入Python 調用的典型方法現在變成:
- PyGILState_STATE gstate;
- gstate = PyGILState_Ensure();
- /*Perform Python actions here.*/
- result = CallSomeFunction();
- /*evaluate result*/
- /*Release the thread. No Python API allowed beyond this point.*/
- PyGILState_Release(gstate);
Note that the PyGILState_*() functions assume there is only one global interpreter (created automatically by Py_Initialize()). Python still supports the creation of additional interpreters (using Py_NewInterpreter()), but mixing multiple interpreters and the PyGILState_*() API is unsupported.
注意 PyGILState_*() 函數假定只有一個全局解釋器(由 Py_Initialize() 自動創建)。Python 還支持創建附加的解釋器(通過 Py_NewInterpreter()),但是 PyGILState_*() 不支持混合多解釋器。
1. PyInterpreterState
This data structure represents the state shared by a number of cooperating threads. Threads belonging to the same interpreter share their module administration and a few other internal items. There are no public members in this structure.
這個數據結構描述幾個協作線程共享的狀態。屬於同一個解釋器的線程共享它們的模塊維護和幾個其它的內部子項。這個結構沒有公開成員。
Threads belonging to different interpreters initially share nothing, except process state like available memory, open file descriptors and such. The global interpreter lock is also shared by all threads, regardless of to which interpreter they belong.
屬於不同解釋器的線程除了可用內存、打開的文件描述符之類的進程狀態不共享任何東西。全局解釋器鎖也由所有線程共享,與它們所屬的解釋器無關。
2. PyThreadState
This data structure represents the state of a single thread. The only public data member is PyInterpreterState *interp, which points to this thread's interpreter state.
這個數據結構描述了單個線程的狀態。唯一的數據成員是 PyInterpreterState *interp,這個線程的解釋器狀態。
3. void PyEval_InitThreads( )
Initialize and acquire the global interpreter lock. It should be called in the main thread before creating a second thread or engaging in any other thread operations such as PyEval_ReleaseLock() or PyEval_ReleaseThread(tstate). It is not needed before calling PyEval_SaveThread() or PyEval_RestoreThread().
初始化和獲取全局解釋器鎖。它應該在主線程中創建,並且應該在第二個線程創建或者類似PyEval_ReleaseLock() 或 PyEval_ReleaseThread(tstate) 之類的線程操作之前。它不需要在PyEval_SaveThread() 或 PyEval_RestoreThread()之前調用。
This is a no-op when called for a second time. It is safe to call this function before calling Py_Initialize().
第二次調用沒有操作(不做任何事情)。它可以在 Py_Initialize() 被調用之前安全調用。
When only the main thread exists, no lock operations are needed. This is a common situation (most Python programs do not use threads), and the lock operations slow the interpreter down a bit. Therefore, the lock is not created initially. This situation is equivalent to having acquired the lock: when there is only a single thread, all object accesses are safe. Therefore, when this function initializes the lock, it also acquires it. Before the Python thread module creates a new thread, knowing that either it has the lock or the lock hasn't been created yet, it calls PyEval_InitThreads(). When this call returns, it is guaranteed that the lock has been created and that the calling thread has acquired it.
只有一個主線程的時候,不需要鎖操作。這是通常的情景(大多數 Python 程序員不用線程),鎖操作稍微拖慢了解釋器。因此,鎖沒有從一開始就創建。這種情況等同於已經獲取了鎖:只有一個線程的時候,所有的對象訪問都是安全的。因此,當該函數初始化鎖,它也可以獲得鎖。Python 線程模塊創建一個新的線程之前,它調用PyEval_InitThreads(),了解有鎖或者還沒有創建鎖。當這個調用返回時,它確保鎖以被創建,並且調用的線程已經得到它。
It is not safe to call this function when it is unknown which thread (if any) currently has the global interpreter lock.
當前擁有全局解釋器鎖的線程(或其它什么)未知時,調用這個函數不安全。
This function is not available when thread support is disabled at compile time.
編譯時如果不支持線程,這個函數不可用。
4. int PyEval_ThreadsInitialized( )
Returns a non-zero value if PyEval_InitThreads() has been called. This function can be called without holding the lock, and therefore can be used to avoid calls to the locking API when running single-threaded. This function is not available when thread support is disabled at compile time. New in version 2.4.
如果 PyEval_InitThreads() 已經被調用,這個函數返回非0值。因為單線程的時候可以不調用鎖 API,這個函數可以在沒有獲得鎖的情況下使用。這個函數在編譯時禁用線程支持的情況下不可用。2.4版新加入。
5. void PyEval_AcquireLock( )
Acquire the global interpreter lock. The lock must have been created earlier. If this thread already has the lock, a deadlock ensues. This function is not available when thread support is disabled at compile time.
獲取全局解釋器鎖。鎖必須提前創建。如果線程已經得到鎖,會發生死鎖。這個函數在編譯時禁用線程支持的情況下不可用。
6. void PyEval_ReleaseLock( )
Release the global interpreter lock. The lock must have been created earlier. This function is not available when thread support is disabled at compile time.
釋放全局解釋器鎖。鎖必須提前創建。這個函數在編譯時禁用線程支持的情況下不可用。
7. void PyEval_AcquireThread( PyThreadState *tstate)
Acquire the global interpreter lock and set the current thread state to tstate, which should not be NULL. The lock must have been created earlier. If this thread already has the lock, deadlock ensues. This function is not available when thread support is disabled at compile time.
獲得全局解釋器鎖並將當前線程狀態設定為 tstate ,它不能為NULL。鎖必須提前創建。如果線程已經擁有鎖,會發生死鎖。這個函數在編譯時禁用線程支持的情況下不可用。
8. void PyEval_ReleaseThread( PyThreadState *tstate)
Reset the current thread state to NULL and release the global interpreter lock. The lock must have been created earlier and must be held by the current thread. The tstate argument, which must not be NULL, is only used to check that it represents the current thread state -- if it isn't, a fatal error is reported. This function is not available when thread support is disabled at compile time.
重置當前線程狀態為NULL並釋放全局解釋器鎖。鎖必須提前創建並且以在當前線程中獲得。參數tstate 不能為 NULL。它只能用於校驗它描述的當前線程狀態——如果它不對,會報告一個致命錯誤。這個函數在編譯時禁用線程支持的情況下不可用。
9. PyThreadState* PyEval_SaveThread( )
Release the interpreter lock (if it has been created and thread support is enabled) and reset the thread state to NULL, returning the previous thread state (which is not NULL). If the lock has been created, the current thread must have acquired it. (This function is available even when thread support is disabled at compile time.)
釋放解釋器鎖(如果它已經被創建而且定義了線程支持)並且將線程狀態設為 NULL ,返回前一個線程狀態(如果它不為 NULL )。如果鎖已經創建,當前線程必須獲取它。(這個函數甚至在編譯時不支持線程的情況下也能使用)。
10. void PyEval_RestoreThread( PyThreadState *tstate)
Acquire the interpreter lock (if it has been created and thread support is enabled) and set the thread state to tstate, which must not be NULL. If the lock has been created, the current thread must not have acquired it, otherwise deadlock ensues. (This function is available even when thread support is disabled at compile time.)
獲取解釋器鎖(如果支持線程並且鎖已經創建)並設置線程狀態為非空的 tstate。如果鎖已經創建,當前線程必須沒有在之前獲得它,不然會發生死鎖。(這個函數甚至在編譯時不支持線程的情況下也能使用)。
The following macros are normally used without a trailing semicolon; look for example usage in the Python source distribution.
以下的宏通常調用的時候不以分號結尾;可以在發布的 Python 源代碼中找到使用的示例。
11. Py_BEGIN_ALLOW_THREADS
This macro expands to "{ PyThreadState *_save; _save = PyEval_SaveThread();". Note that it contains an opening brace; it must be matched with a following Py_END_ALLOW_THREADS macro. See above for further discussion of this macro. It is a no-op when thread support is disabled at compile time.
這個宏展開為 "{ PyThreadState *_save; _save = PyEval_SaveThread();" 。注意它包含一個左大括號;它必須在其后匹配 Py_END_ALLOW_THREADS 宏。這個宏的介紹參見后面。當線程支持在編譯時被禁用時它是一個 no-op。
12. Py_END_ALLOW_THREADS
This macro expands to "PyEval_RestoreThread(_save); }". Note that it contains a closing brace; it must be matched with an earlier Py_BEGIN_ALLOW_THREADS macro. See above for further discussion of this macro. It is a no-op when thread support is disabled at compile time.
這個宏展開為 "PyEval_RestoreThread(_save); }" 。注意它包含一個右大括號;它必須在之前匹配一個Py_BEGIN_ALLOW_THREADS 宏。這個宏的介紹參見前面。當線程支持在編譯時被禁用時它是一個 no-op。
13. Py_BLOCK_THREADS
This macro expands to "PyEval_RestoreThread(_save);": it is equivalent to Py_END_ALLOW_THREADS without the closing brace. It is a no-op when thread support is disabled at compile time.
這個宏展開為 "PyEval_RestoreThread(_save);" ;它等同於 Py_END_ALLOW_THREADS 去掉右大括號。當線程支持在編譯時被禁用時它是一個 no-op。
14. Py_UNBLOCK_THREADS
This macro expands to "_save = PyEval_SaveThread();": it is equivalent to Py_BEGIN_ALLOW_THREADS without the opening brace and variable declaration. It is a no-op when thread support is disabled at compile time.
這個宏展開為 "_save = PyEval_SaveThread();" ;它是等同於 Py_BEGIN_ALLOW_THREADS 去掉左大括號和變量聲明。當線程支持在編譯時被禁用時它是一個 no-op。
All of the following functions are only available when thread support is enabled at compile time, and must be called only when the interpreter lock has been created.
以下所有函數只能在編譯時確認支持線程的情況下可用,並且必須在解釋器鎖創建后被調用。
15. PyInterpreterState* PyInterpreterState_New( )
Create a new interpreter state object. The interpreter lock need not be held, but may be held if it is necessary to serialize calls to this function.
創建一個新解釋器狀態對象。不必要捕獲解釋器鎖,但是當需要同步調用這個函數進行序列化的時候可能需要鎖定。
16. void PyInterpreterState_Clear( PyInterpreterState *interp)
Reset all information in an interpreter state object. The interpreter lock must be held.
重置解釋器狀態對象中的所有信息。解釋器鎖必須被獲取。
17. void PyInterpreterState_Delete( PyInterpreterState *interp)
Destroy an interpreter state object. The interpreter lock need not be held. The interpreter state must have been reset with a previous call to PyInterpreterState_Clear().
析構一個解釋器狀態對象。解釋器鎖需要獲取。解釋器對象必須預先用 PyInterpreterState_Clear() 重置。
18. PyThreadState* PyThreadState_New( PyInterpreterState *interp)
Create a new thread state object belonging to the given interpreter object. The interpreter lock need not be held, but may be held if it is necessary to serialize calls to this function.
創建一個從屬於給定解釋器的新線程狀態對象。解釋器鎖不需要捕獲,但是需要同步調用該函數時可能需要捕獲。
19. void PyThreadState_Clear( PyThreadState *tstate)
Reset all information in a thread state object. The interpreter lock must be held.
重置指定線程狀態對象的所有信息。解釋器鎖必須捕獲。
20. void PyThreadState_Delete( PyThreadState *tstate)
Destroy a thread state object. The interpreter lock need not be held. The thread state must have been reset with a previous call to PyThreadState_Clear().
銷毀一個線程狀態對象。不需要捕獲解釋器鎖。線程狀態必須提前調用 PyThreadState_Clear() 進行清除。
21. PyThreadState* PyThreadState_Get( )
Return the current thread state. The interpreter lock must be held. When the current thread state is NULL, this issues a fatal error (so that the caller needn't check for NULL).
返回當前解釋器狀態。必須捕獲解釋器鎖。當前線程狀態如果為 NULL,發生一個致命錯誤(因此調用者不需要校驗NULL)。
22. PyThreadState* PyThreadState_Swap( PyThreadState *tstate)
Swap the current thread state with the thread state given by the argument tstate, which may be NULL. The interpreter lock must be held.
將當前線程狀態與給定的參數 tstate 交換,tstate可能為 NULL。解釋器鎖必須被捕獲。
23. PyObject* PyThreadState_GetDict( )
Return value: Borrowed reference.
返回值:托管引用。
Return a dictionary in which extensions can store thread-specific state information. Each extension should use a unique key to use to store state in the dictionary. It is okay to call this function when no current thread state is available. If this function returns NULL, no exception has been raised and the caller should assume no current thread state is available. Changed in version 2.3: Previously this could only be called when a current thread is active, and NULL meant that an exception was raised.
返回可存儲線程獨立的狀態信息的一個擴展字典。每個擴展需要一個唯一鍵用於在字典中保存狀態。當前線程狀態不可用的時候它也可以調用。如果這個函數返回 NULL,沒有拋出異常,調用者會假定當前線程狀態無效。自 2.3 版以后的修改:以前它只能在當前線程激活的情況下被調用,如果返回 NULL 就意味着發生了異常。
24. int PyThreadState_SetAsyncExc( long id, PyObject *exc)
Asynchronously raise an exception in a thread. The id argument is the thread id of the target thread; exc is the exception object to be raised. This function does not steal any references to exc. To prevent naive misuse, you must write your own C extension to call this. Must be called with the GIL held. Returns the number of thread states modified; this is normally one, but will be zero if the thread id isn't found. If exc is NULL, the pending exception (if any) for the thread is cleared. This raises no exceptions. New in version 2.3.
在 線程中異步拋出一個異常。參數 id 是目標線程的線程 id ;exc 是要拋出的異常對象。這個函數不獲取 exc 的任何引用。為了防止低級錯誤,你必須自己編寫你的 C 擴展來調用它。調用必須捕獲GIL。返回線程狀態修改數;通常為 1 ,但是如果線程 id 沒有找到就會返回0。如果 exc 是 NULL,所有異常(任何可能)都會從線程中清除。不拋出異常。2.3版新加入。
25. PyGILState_STATE PyGILState_Ensure( )
Ensure that the current thread is ready to call the Python C API regardless of the current state of Python, or of its thread lock. This may be called as many times as desired by a thread as long as each call is matched with a call to PyGILState_Release(). In general, other thread-related APIs may be used between PyGILState_Ensure() and PyGILState_Release() calls as long as the thread state is restored to its previous state before the Release(). For example, normal usage of the Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS macros is acceptable.
確保當前線程已經可以調用與當前 Python 狀態無關的 Python C API,或者它的線程鎖。當一個線程每次希望匹配到 PyGILState_Release() 調用時可能會反復調用這個函數。通常,在線程狀態恢復為Release() 之前的狀態時,其它線程相關的 API 可能會在一對 PyGILState_Ensure() 和PyGILState_Release() 之間調用。例如,通常可以用於Py_BEGIN_ALLOW_THREADS 宏Py_END_ALLOW_THREADS。
The return value is an opaque "handle" to the thread state when PyGILState_Acquire() was called, and must be passed to PyGILState_Release() to ensure Python is left in the same state. Even though recursive calls are allowed, these handles cannot be shared - each unique call to PyGILState_Ensure must save the handle for its call to PyGILState_Release.
PyGILState_Acquire ()被調用的時候,返回值是一個不透明的線程狀態“句柄”,Python離開當前狀態時一定會被被傳遞到 PyGILState_Release() 。甚至盡管允許遞歸調用,這些句柄也不能共享——每次調用 PyGILState_Ensure 都是唯一的,它們的句柄對應它們的PyGILState_Release。
When the function returns, the current thread will hold the GIL. Failure is a fatal error. New in version 2.3.
當函數返回,當前線程將會捕獲 GIL ,失敗會造成致命錯誤。2.3版新增。
26. void PyGILState_Release( PyGILState_STATE)
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 GILState API.)
釋放所有之前獲取的資源。這個調用之后,Python的狀態會與之前 PyGILState_Ensure 調用一致(但是通常這個狀態對調用者是未知的,因此使用 GILState API)。
Every call to PyGILState_Ensure() must be matched by a call to PyGILState_Release() on the same thread. New in version 2.3.
每次調用 PyGILState_Ensure() 都要在同一線程對應調用 PyGILState_Release() 。2.3版本新增。
========================================================================
http://blog.csdn.net/liguangyi/archive/2007/06/20/1659697.aspx
一、首先定義一個封裝類,主要是保證PyGILState_Ensure, PyGILState_Release配對使用,而且這個類是可以嵌套使用的。
- #include <python.h>
- class PyThreadStateLock
- {
- public:
- PyThreadStateLock(void)
- {
- state = PyGILState_Ensure( );
- }
- ~PyThreadStateLock(void)
- {
- PyGILState_Release( state );
- }
- private:
- PyGILState_STATE state;
- };
二、在主線程中,這樣處理
- // 初始化
- Py_Initialize();
- // 初始化線程支持
- PyEval_InitThreads();
- // 啟動子線程前執行,為了釋放PyEval_InitThreads獲得的全局鎖,否則子線程可能無法獲取到全局鎖。
- PyEval_ReleaseThread(PyThreadState_Get());
- // 其他的處理,如啟動子線程等
- ......
- // 保證子線程調用都結束后
- PyGILState_Ensure();
- Py_Finalize();
- // 之后不能再調用任何python的API
三、在主線程,或者子線程中,調用python本身函數的都采用如下處理
- {
- class PyThreadStateLock PyThreadLock;
- // 調用python的API函數處理
- ......
- }
四、另外還有兩個和全局鎖有關的宏,Py_BEGIN_ALLOW_THREADS 和 Py_END_ALLOW_THREADS。這兩個宏是為了在較長時間的C函數調用前,臨時釋放全局鎖,完成后重新獲取全局鎖,以避免阻塞其他 python的線程繼續運行。這兩個宏可以這樣調用
- {
- class PyThreadStateLock PyThreadLock;
- // 調用python的API函數處理
- ......
- Py_BEGIN_ALLOW_THREADS
- // 調用需要長時間的C函數
- ......
- Py_END_ALLOW_THREADS
- // 調用python的API函數處理
- ......
- }
========================================================================
都來自網上,經本人整理。
另外,我昨天發現一篇博客,對這個問題也講得比較清楚,如下,大家可以去看看
