我正在阅读许多关于GVL的文章,并试图了解它的功能。
我目前的理解是:
- GVL是一种全局Mutex,只允许一个本地线程执行Ruby代码。
- 只有持有GVL的线程才能执行Ruby代码
- 然而,如果持有GVL的线程执行I/O操作,则该线程释放GVL,使得另一个线程获取GVL,从而导致并发运行。
因此,如果我编写的代码不包含任何I/O操作,我可以安全地假设不存在由于并发而导致的竞争条件。
在编写了以下代码之后,我注意到变量to
在某种程度上超过了10000000
(初始值from.
)。
from = 100000000
to = 0
50.times.map do
Thread.new do
while from > 0
from -= 1
to += 1
end
end
end.map(&:join)
puts "from: #{from}"
puts "to: #{to}"
to
总是通过在代码中插入Mutex来得到100000000
,但为什么会这样呢?据我所知,由于GVL,while from > 0
条件只由一个线程同时计算,不应该有这样的竞争条件。
我期待的结果是
from: 0
to: 10000000
1条答案
按热度按时间u0sqgete1#
GVL不仅在线程执行IO时被释放。
假设一个线程根本不做任何IO,那么如果GVL只在IO时释放,其他线程将永远不会有机会。
相反,一个线程完全有可能在任何原子操作之后都必须等待其他线程。例如,直接在检查
while from > 0
条件之后或在from -= 1
和to += 1
之间。值得注意的是,
to += 1
甚至不是一个原子操作。它是to = to + 1
的快捷方式,可以在计算右侧和设置新值之间中断。当你真正需要线程安全时,你需要实现互斥锁或信号量。