在不使用其他function的情况下在C扩展中释放全局VM锁定

我不明白为什么在Ruby C API中发布或获取GVL时需要另一级间接。
rb_thread_call_without_gvl()rb_thread_call_with_gvl()都需要一个只接受一个参数的函数,但并非总是如此。 我不想仅仅为了释放GVL而将我的参数包装在结构中。 它使代码的可读性变得复杂,并且需要从void指针进行转换。
在查看Ruby的线程代码后,我发现了与Python的Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS匹配的GVL_UNLOCK_BEGIN / GVL_UNLOCK_END宏,但我找不到有关它们的文档以及它们何时可以安全使用。
rb_thread_call_without_gvl()也使用了BLOCKING_REGION宏,但是我不确定在不调用rb_thread_call_without_gvl()本身的情况下将它作为独立使用是否安全。

在不必调用其他函数的情况下,在执行流程中安全释放GVL的正确方法是什么?

在Ruby 2.x中, 只有 rb_thread_call_without_gvl API。 GVL_UNLOCK_BEGINGVL_UNLOCK_END是仅在thread.c定义的实现细节,因此对Ruby扩展不可用 。 因此,对你的问题的直接回答是“没有办法正确安全地释放GVL而不调用另一个function”。

之前有一个“基于区域”的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/ CAPI弃用计划的wiki / CAPI_obsolete_definitions 。

因此,遗憾的是,您遇到了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); ... } 

虽然这并没有解决你原来的问题,但它确实至少使它更容易忍受(我希望)。

在不必调用其他函数的情况下,在执行流程中安全释放GVL的正确方法是什么?

您必须使用提供的API或您使用的任何方法最终会破坏。 GVL的API在thread.h定义

 void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1); void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2); void *rb_thread_call_without_gvl2(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2); 

您在标题中找到的是您与API的消费者和API的作者之间达成的协议。 把它想象成一份合同。 除非在标题中找到,否则您在.c中找到的特定静态方法和MACROS中的任何内容都不能在文件外部使用。 static关键字可以防止这种情况发生,这是它存在的原因之一,它在C中最重要。你提到的其他项目都在thread.c 。 你可以在thread.cthread.c但是使用它的任何东西都违反了API的合同,即它不安全,永远不会。

我不是建议你这样做,但是你做你想做的事的唯一方法是将他们的部分实现复制到你自己的代码中,这不会通过代码审查。 您需要复制的代码量可能使您安全使用其API所需的任何操作都相形见绌。