英文:
Why do the following ruby codes show race conditions despite GVL?
问题
I understand your request, and I'll provide the translated version of the content you provided without the code part:
我正在阅读许多关于GVL的文章,试图理解它的作用。
我目前的理解是:
- GVL是一种全局互斥锁,允许只有一个本地线程执行Ruby代码。
- 只有持有GVL的线程才能执行Ruby代码。
- 但是,如果持有GVL的线程进行I/O操作,该线程会释放GVL,以便另一个线程获得GVL并同时运行。
因此,如果我编写的代码不包含任何I/O操作,我可以安全地假设没有由于并发性而引起的竞争条件。
关于代码部分的问题,您提到在插入Mutex的情况下,to
始终为 100000000
。这是因为在没有Mutex的情况下,多个线程可以同时访问和修改 to
变量,导致竞争条件。通过使用Mutex,您可以确保一次只有一个线程可以修改 to
变量,因此得到了预期的结果。
英文:
I'm reading many articles about GVL and trying to understand what it does.
My current understanding is:
- GVL is a sort-of global Mutex to allow only one native thread to execute ruby codes.
- Only the thread that holds GVL can execute ruby codes
- However, if a thread that holds GVL does I/O things, the thread releases GVL so that another thread acquires GVL resulting in running concurrently.
Therefore, if I wrote codes that do not contain any I/O operation, I can safely assume there is no race condition due to concurrency.
Having written the following codes, I noticed that the variable to
exceeded 10000000
(initial from.
value) to some extent.
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
always results 100000000
by inserting Mutex into that code, but why is that happen?
As of my understanding, the condition while from > 0
is evaluated by only one thread thanks to GVL at the same time, and there should not be such race conditions.
I expected the result like
from: 0
to: 10000000
答案1
得分: 1
GVL 不仅在线程进行 IO 时释放。
想象一下,如果一个线程根本不进行任何 IO 操作,那么如果 GVL 仅在 IO 时释放,其他线程将永远不会有机会。
相反,完全有可能一个线程在任何原子操作之后都必须等待其他线程。例如,在检查 while from > 0
条件之后或在 from -= 1
和 to += 1
之间。
值得注意的是,to += 1
甚至不是一个原子操作。它是 to = to + 1
的一种快捷方式,它可以在计算右侧值和设置新值之间被中断。
当你真正需要线程安全时,你需要实现互斥锁或信号量。
英文:
The GVL is not only released when a thread does IO.
Imagine a thread not doing any IO at all, then the other threads would never get a chance if GVL was only released on IO.
Instead, it is entirely possible that a thread has to wait for other threads after any atomic operation. For example, directly after checking the while from > 0
condition or between from -= 1
and to += 1
.
And it is worth noting that to += 1
is not even an atomic operation. It is a shortcut for to = to + 1
and it can be interrupted between evaluating the right side and setting the new value.
When you really need thread safety, then you need to implement mutexs or semaphores.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论