【问题标题】:Ruby please give a simple NON-thread safe exampleRuby 请给出一个简单的非线程安全示例
【发布时间】:2017-02-11 18:43:46
【问题描述】:

有人可以举一个具体的例子来证明非线程安全吗? (如果可能,与我的功能版本类似)

我需要一个示例类来演示非线程安全操作,以便我可以断言失败,然后强制执行互斥锁,以便我可以测试我的代码是否是线程安全的。

我尝试了以下但没有成功,因为线程似乎没有并行运行。假设 ruby​​ += 运算符不是线程安全的,这个测试总是在它不应该通过的时候通过:

class TestLock
  attr_reader :sequence

  def initialize
    @sequence = 0
  end

  def increment
    @sequence +=  1
  end
end

#RSpec test
it 'does not allow parallel calls to increment' do
  test_lock = TestLock.new
  threads = []
  list1 = []
  list2 = []
  start_time = Time.now + 2

  threads << Thread.new do
    loop do
      if Time.now > start_time
        5000.times { list1 << test_lock.increment }
        break
      end
    end
  end

  threads << Thread.new do
    loop do
      if Time.now > start_time
        5000.times { list2 << test_lock.increment }
        break
      end
    end
  end

  threads.each(&:join) # wait for all threads to finish
  expect(list1 & list2).to eq([])
end

【问题讨论】:

  • 线程是不确定的。这就是让他们如此丑陋的原因。你不能让它们确定性地失败。即使您的锁定不正确,它们有时也会正常工作。你用你的代码完美地证明了这一点,它在 YARV 上工作,在 JRuby 和 Rubinius 上失败。

标签: ruby-on-rails ruby multithreading


【解决方案1】:

这是一个示例,它使用阻塞文件写入,而不是通过添加、连接或类似的方式查找竞争条件。

总结部分:

  • file_write 方法执行 2 秒的阻塞写入。
  • file_read 读取文件并将其分配给要在其他地方引用的全局变量。
  • NonThreadsafe#test 在它们自己的线程中连续调用这些方法,没有互斥体。在调用之间插入sleep 0.2,以确保在尝试读取时阻塞文件写入已经开始。 join 在第二个线程上被调用,所以我们确定它将读取值设置为全局变量。它从全局变量返回读取值。
  • Threadsafe#test 做同样的事情,但将每个方法调用包装在互斥体中。

这里是:

module FileMethods
  def file_write(text)
    File.open("asd", "w") do |f|
      f.write text
      sleep 2
    end
  end
  def file_read
    $read_val = File.read "asd"
  end
end

class NonThreadsafe
  include FileMethods
  def test
    `rm asd`
    `touch asd`
    Thread.new { file_write("hello") }
    sleep 0.2
    Thread.new { file_read }.join
    $read_val
  end
end

class Threadsafe
  include FileMethods
  def test
    `rm asd`
    `touch asd`
    semaphore = Mutex.new
    Thread.new { semaphore.synchronize { file_write "hello" } }
    sleep 0.2
    Thread.new { semaphore.synchronize { file_read } }.join
    $read_val
  end
end

和测试:

expect(NonThreadsafe.new.test).to be_empty
expect(Threadsafe.new.test).to eq("hello")

至于解释。非线程安全将文件的读取 val 显示为空的原因是因为在读取发生时阻塞写入操作仍在发生。但是,当您使用同步互斥锁时,写入将在读取之前完成。另请注意,线程安全示例中的.join 比非线程安全值中的花费更长 - 这是因为它在写入线程中指定的整个持续时间内处于休眠状态。

【讨论】:

    猜你喜欢
    • 2016-11-29
    • 1970-01-01
    • 1970-01-01
    • 2011-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多