我在使用 C 扩展中的线程异步运行 ruby 代码时遇到问题。
我有以下 C 代码:
struct DATA {
VALUE callback;
pthread_t watchThread;
void *ptr;
};
void *executer(void *ptr) {
struct DATA *data = (struct DATA *) ptr;
char oldVal[20] = "1";
char newVal[20] = "1";
pthread_cleanup_push(&threadGarbageCollector, data);
while(1) {
if(triggerReceived) {
rb_funcall(data->callback, rb_intern("call"), 0);
}
}
pthread_cleanup_pop(1);
return NULL;
}
VALUE spawn_thread(VALUE self) {
VALUE block;
struct DATA *data;
Data_Get_Struct(self, struct DATA, data);
block = rb_block_proc();
data->callback = block;
pthread_create(&data->watchThread, NULL, &executer, data);
return self;
}
我使用它是因为我想提供 ruby 代码作为回调,一旦线程收到信号,该回调就会被执行。
一般来说,如果回调是这样的 ruby 代码,那么这工作得很好:
1 + 1
但是,如果回调 ruby 代码如下所示:
puts "test"
一旦回调被执行,主 ruby 进程将停止响应。
该线程仍在运行,并且能够对信号做出反应,并在每次线程收到消息时进行“测试”。
有人可以告诉我,如何解决这个问题吗?
多谢
来自 Ruby C API 文档 https://docs.ruby-lang.org/en/trunk/extension_rdoc.html#label-Threading:
从 Ruby 1.9 开始,Ruby 支持使用一个内核的本机 1:1 线程
每个 Ruby Thread 对象都有一个线程。目前有一个GVL(Global VM
Lock),它可以防止同时执行 Ruby 代码,这可能是
由 rb_thread_call_without_gvl 释放并且
rb_thread_call_without_gvl2 函数。这些功能是
使用起来很棘手,并记录在 thread.c 中;之前不要使用它们
阅读 thread.c 中的评论。
太长了; Ruby VM 当前(在撰写本文时)不是线程安全的。查看这篇关于 Ruby 线程的好文章 https://silverhammermba.github.io/emberb/c/#threading以便更好地全面了解如何在这些范围内工作。
你可以使用 Ruby 的native_thread_create(rb_thread_t *th) https://github.com/ruby/ruby/blob/ruby_2_5/thread_pthread.c#L986这将使用pthread_create
在幕后。您可以在方法定义上方的文档中了解一些缺点。然后您可以使用 Ruby 运行您的回调rb_thread_call_with_gvl https://github.com/ruby/ruby/blob/ruby_2_5/thread.c#L1531方法。另外,我还没有在这里完成它,但创建一个包装方法可能是一个好主意,这样您就可以使用rb_protect
处理回调可能引发的异常(否则它们将被虚拟机吞掉)。
VALUE execute_callback(VALUE callback)
{
return rb_funcall(callback, rb_intern("call"), 0);
}
// execute your callback when the thread receives signal
rb_thread_call_with_gvl(execute_callback, data->callback);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)