且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Python C API-线程安全吗?

更新时间:2021-11-26 22:05:40

在运行C代码时,Python不会释放GIL(除非您告知它).它仅在字节码指令之前(而不是在此期间)释放GIL,并且从解释器的角度来看,运行C函数是执行CALL_FUNCTION字节码的一部分.* (不幸的是,我找不到引用对于本段,但我几乎可以肯定是对的)

Python will not release the GIL when you are running C code (unless you tell it to). It only releases the GIL just before a bytecode instruction (not during) and from the interpreter's point of view running a C function is part of executing the CALL_FUNCTION bytecode.* (Unfortunately I can't find a reference for this paragraph currently, but I'm almost certain it's right)

因此,除非您进行任何特定的操作,否则C代码将是唯一正在运行的线程,因此您在其中执行的任何操作都应该是线程安全的.

Therefore, unless you do anything specific your C code will be the only thread running and thus any operation you do in it should be thread safe.

如果您特别想发布GIL-例如,因为您要进行长时间的计算而不会干扰Python,从文件读取或在等待其他事情发生时休眠-那么最简单的方法是做 Py_BEGIN_ALLOW_THREADS然后Py_END_ALLOW_THREADS当您想找回它时.在这段代码中,您不能使用大多数Python API函数,并且有责任确保C语言中的线程安全.最简单的方法是仅使用局部变量,而不读取或写入任何全局状态.

If you specifically want to release the GIL - for example because you're doing a long calculation which doesn't interfere with Python, reading from a file, or sleeping while waiting for something else to happen - then the easiest way is to do Py_BEGIN_ALLOW_THREADS then Py_END_ALLOW_THREADS when you want to get it back. During this block you cannot use most Python API functions and it's your responsibility to ensure thread safety in C. The easiest way to do this is to only use local variables and not read or write any global state.

如果您已经在没有GIL(线程A)的情况下运行了C线程,那么仅将GIL保持在线程B中就不能保证线程A不会修改C全局变量.为了安全起见,您需要确保在所有C函数中都没有某种锁定机制(Python GIL或C机制)的情况下,永远不要修改全局状态.

If you've already got a C thread running without the GIL (thread A) then simply holding the GIL in thread B does not guarantee that thread A won't modify C global variables. To be safe you need to ensure that you never modify global state without some kind of locking mechanism (either the Python GIL or a C mechanism) in all your C functions.

其他思想

*可以用C代码发布GIL的地方之一是,如果C代码调用了导致执行Python代码的操作.这可能是通过使用PyObject_Call来完成的.如果Py_DECREF导致要执行析构函数,则不太明显.您可以在恢复C代码时恢复GIL,但是您不能再保证全局对象不变.这种明显的影响不会影响像x++这样的简单C.

* One place where the GIL can be released in C code is if the C code calls something that causes Python code to be executed. This might be through using PyObject_Call. A less obvious place would be if Py_DECREF caused a destructor to be executed. You'd have the GIL back by the time your C code resumed, but you could no longer guarantee that global objects were unchanged. This obvious doesn't affect simple C like x++.