我不明白为什么在发布或获取 Ruby C API 中的 GVL 时需要另一个间接级别。
rb_thread_call_without_gvl()
和 rb_thread_call_with_gvl()
都需要一个只接受一个参数的函数,但情况并非总是如此。
我不想仅仅为了发布 GVL 而将我的参数包装在一个结构中。它使代码的可读性变得复杂,并且需要从 void 指针转换到 void 指针。
在查看 Ruby 的线程代码后,我找到了 GVL_UNLOCK_BEGIN
。/GVL_UNLOCK_END
与 Python 的 Py_BEGIN_ALLOW_THREADS
/Py_END_ALLOW_THREADS
匹配的宏但我找不到关于它们以及何时可以安全使用它们的文档。
还有 BLOCKING_REGION
rb_thread_call_without_gvl()
中使用了宏但我不确定在不调用 rb_thread_call_without_gvl()
本身的情况下将它作为独立使用是否安全。
在执行流程中间安全释放 GVL 而不必调用其他函数的正确方法是什么?
最佳答案
在 Ruby 2.x 中,只有 rb_thread_call_without_gvl
API。 GVL_UNLOCK_BEGIN
和 GVL_UNLOCK_END
是仅在 thread.c
中定义的实现细节,因此对 Ruby 扩展不可用 .因此,您问题的直接答案是“没有办法在不调用另一个函数的情况下正确和安全地释放 GVL”。
以前有一个“基于区域”的 API,rb_thread_blocking_region_begin
/rb_thread_blocking_region_end
,但是这个 API 在 Ruby 1.9.3 中被弃用并在 Ruby 2.2 中被移除(参见 https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/CAPI_obsolete_definitions CAPI 弃用时间表)。
因此,不幸的是,您被 rb_thread_call_without_gvl
困住了。
也就是说,您可以采取一些措施来缓解疼痛。在标准 C 中,大多数指针和 void *
之间的转换是隐式的,因此您不必添加强制转换。此外,使用指定的初始化语法可以简化参数结构的创建。
因此,你可以这样写
struct my_func_args {
int arg1;
char *arg2;
};
void *func_no_gvl(void *data) {
struct my_func_args *args = data;
/* do stuff with args->arg... */
return NULL;
}
VALUE my_ruby_function(...) {
...
struct my_func_args args = {
// designated initializer syntax (C99) for cleaner code
.arg1 = ...,
.arg2 = ...,
};
// call without an unblock function
void *res = rb_thread_call_without_gvl(func_no_gvl, &args, NULL, NULL);
...
}
虽然这不能解决您原来的问题,但它至少使它更容易被接受(我希望如此)。
关于c - 在不使用其他功能的情况下释放 C 扩展中的全局 VM 锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36245878/