从ruby c扩展中的线程调用IO操作将导致ruby挂起

时间:2014-09-16 21:46:10

标签: c ruby multithreading ruby-c-extension

我在使用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-code作为回调,一旦Thread收到信号就会执行回调。

一般情况下,如果回调类似于这个ruby-code:

,这样可以正常工作
1 + 1

但是,如果回调ruby-code看起来像这样:

puts "test"
一旦回调被执行,

主ruby进程将停止响应。 线程仍然在运行,能够对信号作出反应,并将" test"每次,线程都会收到一条消息。

有人可以告诉我,如何解决这个问题?

非常感谢

1 个答案:

答案 0 :(得分:1)

From the Ruby C API docs

  

从Ruby 1.9开始,Ruby支持一个内核的本机1:1线程   每个Ruby线程对象的线程数。当前,有一个GVL(全局VM   锁定)可防止同时执行可能是   由rb_thread_call_without_gvl发布,并且   rb_thread_call_without_gvl2函数。这些功能是   难以使用,并记录在thread.c中;之前不要使用它们   在thread.c中阅读评论。

TLDR; Ruby VM当前(在编写本文时)不是线程安全的。请访问this nice write-up on Ruby Threading,以更好地全面了解如何在这些范围内工作。

您可以使用Ruby的native_thread_create(rb_thread_t *th),后者将在幕后使用pthread_create。您可以在方法定义上方的文档中了解一些缺点。然后,您可以使用Ruby的rb_thread_call_with_gvl方法运行回调。另外,我在这里还没有做过,但是创建一个包装方法可能是一个好主意,因此您可以使用rb_protect处理回调可能引发的异常(否则它们将被VM吞噬)。 / p>

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);