【问题标题】:Ruby thread safe thread creationRuby 线程安全线程创建
【发布时间】:2019-08-26 15:38:20
【问题描述】:

我今天遇到了这段代码:

@thread ||= Thread.new do
  # this thread should only spin up once
end

它被多个线程调用。我担心如果多个线程正在调用此代码,您可能会创建多个线程,因为@thread 访问不同步。但是,有人告诉我,由于 Global Interpreter Lock,这不可能发生。我对 Ruby 中的线程做了一些阅读,似乎运行 Ruby 代码的单个线程可能会被其他线程抢占。如果是这样的话,你不能有这样的交错:

Thread A             Thread B
========             ========
Read from @thread    .
Thread.New           .
[Thread A preempted] .
.                    Read from @thread
.                    Thread.New
.                    Write to @thread
Write to @thread

此外,由于对@thread 的访问不同步,是否保证对所有其他线程可见对@thread 的写入?我过去使用的其他语言的内存模型不能保证写入内存的可见性,除非您使用原子、互斥锁等同步对该内存的访问。

我仍在学习 Ruby,并意识到要理解 Ruby 中的并发性还有很长的路要走。对此的任何帮助将不胜感激!

【问题讨论】:

  • ||= 不是线程安全的,除非您使用 Mutex 来锁定它。这不是原子操作,而是三个操作(读取、测试、设置),其中有很多线程问题的空间。
  • @tadman 这是我的怀疑。我正在做一个实验来尝试展示这种交错。但是,我不知道如何鼓励线程调度程序以高速率抢占。
  • 如果您需要这样做,请使用Mutex#synchronize

标签: ruby multithreading concurrency thread-safety


【解决方案1】:

你需要一个互斥锁。本质上,GIL 保护您免受的唯一事情是访问未初始化的内存。如果 Ruby 中的某些东西可以被很好地定义而不是原子的,那么你不应该假设它是原子的。

一个简单的例子表明您的示例排序是可能的。每次运行时都会收到“双重设置”消息:

$global = nil
$thread = nil

threads = []

threads = Array.new(1000) do
  Thread.new do
    sleep 1
    $thread ||= Thread.new do
      if $global
        warn "double set!"
      else
        $global = true
      end
    end
  end
end

threads.each(&:join)

【讨论】:

  • 谢谢@Max。我在实验中做了非常相似的事情。事实上,我的怀疑是有道理的。
猜你喜欢
  • 2010-10-13
  • 1970-01-01
  • 2016-05-05
  • 1970-01-01
  • 2021-06-04
  • 1970-01-01
  • 2012-04-08
  • 2022-11-21
  • 2020-09-28
相关资源
最近更新 更多