访问 cython 扩展类型成员时 nogil 是安全的
is nogil safe when accessing cython extension type members
来自:
You should be able to access [an extension type's] cdef members [inside a nogil block]... and call their cdef functions that are marked as nogil.
然而,cython documentation不同意:
After the GIL is released, any operation that involves python objects must first reacquire the GIL.
我假设“python 对象”包括 cython 扩展类型。这让我认为下面的伪代码是不安全的,因为它包含了一个竞争条件,这是由于在没有 GIL 的情况下修改 python 对象而引起的:
def function(ExtensionType arg):
with nogil:
# long running task
# modify arg's member
arg = ExtensionType()
function(arg)
# access arg's member
# (alternatively, accessing and modifying the member could be swapped, with the same issue)
我已将其扩展到实际代码中以说明我的困惑:
cimport cytime
cdef class Tester:
cdef int val
def get_val(self):
return self.val
def set_val(self):
print("1: GIL acquired by set_val")
with nogil:
cytime.sleep(0.1) # give me the GIL later, I'm not ready for it yet
self.val = 1
print("3: GIL reacquired by set_val")
t = Tester()
t.set_val()
print("2: val should be 0, but actually is: " + str(t.get_val()))
我预计程序的执行会遵循:1、2、3。然而,这是输出:
1: GIL acquired by set_val
3: GIL reacquired by set_val
2: val should be 0, but actually is: 1
谁能解释一下?谢谢。
作为一个猜测,cdef class
个实例被重新计算,因此需要 GIL 保护。
Accessing/writing 到 self.val
没有 GIL 就可以了。你不需要为 self
做任何引用计数(因为你已经有了对它的引用,你不需要另一个)并且你不需要为 [= 做任何引用计数12=] 因为它是 C int
。实际上,您可以在没有 GIL 的情况下对 cdef class
个实例执行合理的操作(例如访问 nogil
cdef
方法)。
Cython 通常会阻止您在 nogil
块中执行需要 GIL 的操作(它并不完美,但通常相当彻底)。
请注意,如果您选择从多个线程访问 .val
,那么您很可能会遇到竞争条件,这完全是您自己的错。我所说的“安全”是指 Python 引用计数状态不会被破坏。
您似乎对 nogil
块的作用有很大的误解,并且将其视为类似于协程的东西!?
当你释放 GIL 时发生的所有事情是它允许让 其他 Python 线程你有 运行。正在处理 set_val
的线程按逻辑顺序继续:它停止并等待一段时间,它设置值,它等待重新获取 GIL,它打印语句 3,它 returns 到全局范围,它打印 statement-2.
来自
You should be able to access [an extension type's] cdef members [inside a nogil block]... and call their cdef functions that are marked as nogil.
然而,cython documentation不同意:
After the GIL is released, any operation that involves python objects must first reacquire the GIL.
我假设“python 对象”包括 cython 扩展类型。这让我认为下面的伪代码是不安全的,因为它包含了一个竞争条件,这是由于在没有 GIL 的情况下修改 python 对象而引起的:
def function(ExtensionType arg):
with nogil:
# long running task
# modify arg's member
arg = ExtensionType()
function(arg)
# access arg's member
# (alternatively, accessing and modifying the member could be swapped, with the same issue)
我已将其扩展到实际代码中以说明我的困惑:
cimport cytime
cdef class Tester:
cdef int val
def get_val(self):
return self.val
def set_val(self):
print("1: GIL acquired by set_val")
with nogil:
cytime.sleep(0.1) # give me the GIL later, I'm not ready for it yet
self.val = 1
print("3: GIL reacquired by set_val")
t = Tester()
t.set_val()
print("2: val should be 0, but actually is: " + str(t.get_val()))
我预计程序的执行会遵循:1、2、3。然而,这是输出:
1: GIL acquired by set_val
3: GIL reacquired by set_val
2: val should be 0, but actually is: 1
谁能解释一下?谢谢。
作为一个猜测,cdef class
个实例被重新计算,因此需要 GIL 保护。
Accessing/writing 到 self.val
没有 GIL 就可以了。你不需要为 self
做任何引用计数(因为你已经有了对它的引用,你不需要另一个)并且你不需要为 [= 做任何引用计数12=] 因为它是 C int
。实际上,您可以在没有 GIL 的情况下对 cdef class
个实例执行合理的操作(例如访问 nogil
cdef
方法)。
Cython 通常会阻止您在 nogil
块中执行需要 GIL 的操作(它并不完美,但通常相当彻底)。
请注意,如果您选择从多个线程访问 .val
,那么您很可能会遇到竞争条件,这完全是您自己的错。我所说的“安全”是指 Python 引用计数状态不会被破坏。
您似乎对 nogil
块的作用有很大的误解,并且将其视为类似于协程的东西!?
当你释放 GIL 时发生的所有事情是它允许让 其他 Python 线程你有 运行。正在处理 set_val
的线程按逻辑顺序继续:它停止并等待一段时间,它设置值,它等待重新获取 GIL,它打印语句 3,它 returns 到全局范围,它打印 statement-2.