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

the*_*row 9 c ruby multithreading

我不明白为什么在Ruby C API中发布或获取GVL时需要另一级别的间接.
双方rb_thread_call_without_gvl()rb_thread_call_with_gvl()要求只接受一个参数,它是情况并非总是如此的功能.我不想仅仅为了释放GVL而将我的参数包装在结构中.它使代码的可读性变得复杂,并且需要从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 的正确方法是什么?

nne*_*neo 7

在Ruby 2.x中,有rb_thread_call_without_gvlAPI.GVL_UNLOCK_BEGIN并且GVL_UNLOCK_END是仅在Ruby扩展中定义的实现细节,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弃用计划的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);
    ...
}
Run Code Online (Sandbox Code Playgroud)

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


Har*_*rry 5

在不必调用其他函数的情况下,在执行流程中安全释放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);
Run Code Online (Sandbox Code Playgroud)

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

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