【问题标题】:how come ruby's single os thread doesn't block while copying a file?为什么ruby的单操作系统线程在复制文件时不会阻塞?
【发布时间】:2013-01-16 03:41:52
【问题描述】:

我的假设:

  • MRI ruby​​ 1.8.X 没有原生线程,只有绿色线程。

  • 操作系统不知道这些绿色线程。

  • 发出大量 IO 操作应暂停整个进程,直到发出适当的 IO 中断。

有了这些,我创建了一个simple ruby program,它执行以下操作:

  • 启动一个打印“工作!”的线程每秒。

  • 发出 IO 请求以在“主”线程上复制一个大 (1gb) 文件。

现在有人会猜测,作为操作系统不可见的绿色线程,它会将整个进程置于“阻塞”队列和“工作!”队列中。绿色线程不会执行。令人惊讶的是,它有效:S

有人知道那里发生了什么吗?谢谢。

【问题讨论】:

  • 我不知道 ruby​​,但是 javascript 是单线程的并且具有非阻塞 IO,当使用服务器端 javascript(例如 node.js)时,发出 fs.readFile 不会阻止执行,而是接受回调参数,当读取完成时调用回调函数。
  • 如果已知线程阻塞了 I/O 操作,那么 Ruby 解释器不能在此期间简单地调度另一个线程吗?
  • @maerics 这不取决于 ruby​​,请求 I/O 必须通过操作系统完成,当操作系统注意到需要阻止某个进程等待 I/O 时,它将暂停它。跨度>
  • @maerics 是对的。被阻塞的线程在等待响应时会休眠,同时解释器会调度另一个线程。

标签: ruby multithreading concurrency io


【解决方案1】:

没有原子内核文件复制操作。这是进入和退出内核的大量相当短的读写操作。

因此,该过程不断地重新获得控制权。信号已传递。

绿色线程通过将 Ruby 级线程调度程序连接到低级 I/O 和信号接收来工作。只要这些钩子周期性地捕获控制,绿色线程就会像更多并发线程一样行事。

Unix 最初为用户进程环境提供了一个完全不感知线程但非常简单的抽象机器模型。

随着时间的推移,对一般并发的支持,特别是线程,以两种不同的方式逐位添加。

  1. 添加了许多小组件来检查 I/O 是否会阻塞,如果 I/O 会阻塞则失败(稍后重试),中断慢速 tty I/O 以获取信号,然后透明地返回它,等等。当 Unix API 被合并时,每个 kludge 都以一种以上的形式存在。 有很多选择。1.
  2. 还添加了对共享地址空间的多个内核可见进程形式的线程的直接支持。这些threads are dangerous and untestable 但被广泛支持和使用。大多数情况下,程序不会崩溃。随着时间的推移,随着硬件支持更真实的并发性,潜在的错误变得可见。我一点也不担心 Ruby 不完全支持 that nightmare.

1.标准的好处是有这么多。

【讨论】:

    【解决方案2】:

    当 MRI 1.9 启动时,它会产生两个本机线程。一个线程用于 VM,另一个用于处理信号。 Rubinis 使用这种策略,JVM 也是如此。管道可用于传递来自其他进程的任何信息。

    关于FileUtils模块,cdpwdmkdirrmlncpmvchmod、@98765433333在某种程度上,987654334@ 方法都使用StreamUtils 子模块的内部 API 外包给 OS 本机实用程序,而第二个线程则等待来自外部进程的信号。由于这些方法是相当线程安全的,因此无需锁定解释器,因此这些方法不会相互阻塞。

    编辑:

    MRI 1.8.7 相当聪明,它知道当 Thread 正在等待一些外部事件(例如浏览器发送 HTTP 请求)时,可以将 Thread 置于睡眠状态,并在检测到数据时将其唤醒. - 来自 Engine Yard 的 Evan Phoenix,Ruby, Concurrency, and You

    FileUtils 的实现基本实现从查看源代码来看 1.8.7 并没有太大变化。 1.8.7 还使用休眠定时器线程来等待 IO 响应。 1.9 的主要区别在于使用原生线程而不是绿色线程。 source code 的线程也更加精致。

    我所说的线程安全是指由于进程之间没有共享任何内容,因此没有理由锁定全局解释器。有一种误解认为 Ruby 在执行某些任务时会“阻塞”。每当一个线程必须阻塞时,即在不使用任何 cpu 的情况下等待,Ruby 只是调度另一个线程。然而,在某些情况下,例如机架服务器使用 20% 的 CPU 等待响应,解锁解释器并允许并发线程在等待期间处理其他请求可能是合适的。从某种意义上说,这些线程是并行工作的。 GIL 使用rb_thread_blocking_region API 解锁。这是subject 的一篇好帖子。

    【讨论】:

    • 我说的是 MRI 1.8.x 作为第一点,因为 1.9.x 有本机线程,这个问题在那个平台上没有意义。您能否指出表明 ruby​​ 中的 IO 确实是非阻塞的来源?你是什​​么意思“不需要GIL”?您可以为代码的某些部分删除它吗?
    • 我进行了编辑以更好地回答您的问题。如果您希望我详细说明任何可能仍含糊不清的观点,请告诉我。
    猜你喜欢
    • 2017-04-14
    • 2020-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 2016-06-26
    • 1970-01-01
    相关资源
    最近更新 更多