从ruby c扩展中的线程调用IO操作将导致ruby挂起 [英] calling IO Operations from thread in ruby c extension will cause ruby to hang

查看:92
本文介绍了从ruby c扩展中的线程调用IO操作将导致ruby挂起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C扩展中使用线程来运行ruby代码异步时遇到问题.

I have a problem with using threads in a C Extension to run ruby code async.

我有以下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代码作为回调,一旦线程接收到信号便将执行该回调.

I am using this because I want to provide ruby-code as a callback, which will be executed, once the Thread receives a signal.

通常,如果回调类似于以下红宝石代码,这可以正常工作:

In general this is working fine, if the callback is something like this ruby-code:

1 + 1

但是,如果回调ruby代码看起来像这样:

But, if the callbacks ruby-code looks like this:

puts "test"

一旦回调被执行,主ruby进程将停止响应. 该线程仍在运行,并且能够对信号做出反应,并在每次收到消息时进行测试".

than the main ruby process will stop responding, once the callback is getting executed. The thread is still running and able to react to signals and puts the "test" everytime, the thread receives a message.

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

Can somebody maybe tell me, how to fix this?

非常感谢

推荐答案

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

As of Ruby 1.9, Ruby supports native 1:1 threading with one kernel thread per Ruby Thread object. Currently, there is a GVL (Global VM Lock) which prevents simultaneous execution of Ruby code which may be released by the rb_thread_call_without_gvl and rb_thread_call_without_gvl2 functions. These functions are tricky-to-use and documented in thread.c; do not use them before reading comments in thread.c.

TLDR; Ruby VM当前(在编写本文时)不是线程安全的.请查看关于Ruby Threading的这篇不错的文章,以更好地全面了解如何在这些范围内工作.

TLDR; the Ruby VM is not currently (at the time of writing) thread safe. Check out this nice write-up on Ruby Threading for a better overall understanding of how to work within these confines.

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

You can use Ruby's native_thread_create(rb_thread_t *th) which will use pthread_create behind the scenes. There are some drawbacks that you can read about in the documentation above the method definition. You can then run your callback with Ruby's rb_thread_call_with_gvl method. Also, I haven't done it here, but it might be a good idea to create a wrapper method so you can use rb_protect to handle exceptions your callback may raise (otherwise they will be swallowed by the VM).

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

这篇关于从ruby c扩展中的线程调用IO操作将导致ruby挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆