以下的 Ruby 代码为什么会显示竞态条件,尽管有 GVL(全局虚拟机锁)?

huangapple go评论66阅读模式
英文:

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.

以下的 Ruby 代码为什么会显示竞态条件,尽管有 GVL(全局虚拟机锁)?

I expected the result like

from: 0
to: 10000000

答案1

得分: 1

GVL 不仅在线程进行 IO 时释放。

想象一下,如果一个线程根本不进行任何 IO 操作,那么如果 GVL 仅在 IO 时释放,其他线程将永远不会有机会。

相反,完全有可能一个线程在任何原子操作之后都必须等待其他线程。例如,在检查 while from > 0 条件之后或在 from -= 1to += 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.

huangapple
  • 本文由 发表于 2023年5月15日 13:32:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76251109.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定