4

我的假设:

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

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

  • 发出一个 IO 繁重的操作应该暂停整个过程,直到发出适当的 IO 中断。

有了这些,我创建了一个简单的 ruby​​ 程序,它执行以下操作:

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

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

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

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

4

2 回答 2

4

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

结果,该过程不断地重新获得控制权。信号被传递。

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

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

随着岁月的流逝,对一般并发的支持,特别是线程以两种不同的方式逐位添加。

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

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

于 2013-01-31T23:26:56.820 回答
3

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

至于FileUtils模块,cd, pwd, mkdir, rm, ln, cp, mv, chmod,chowntouch方法在某种程度上都是使用StreamUtils子模块的内部 API 外包给 OS 本地实用程序的,而第二个线程则等待来自 an 的信号。外部过程。由于这些方法是相当线程安全的,因此无需锁定解释器,因此这些方法不会相互阻塞。

编辑:

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

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

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

于 2013-01-31T22:43:24.090 回答