Ruby MRI 1.8.7 - 文件写入线程安全问题

4

据我所知,Ruby MRI 1.8.7中的文件写入是完全线程安全的。

示例1 - 完美结果:

File.open("test.txt", "a") { |f|
  threads = []
  1_000_000.times do |n|
    threads << Thread.new do
      f << "#{n}content\n"
    end
  end
  threads.each { |t| t.join }
}

例子2-完美的结果(但更慢):
threads = []
100_000.times do |n|
  threads << Thread.new do
    File.open("test2.txt", "a") { |f|
      f << "#{n}content\n"
    }
  end
end
threads.each { |t| t.join }

那么,我无法构建出一个遇到并发问题的情景,你能吗?

如果有人能够解释一下为什么我还应该在这里使用Mutex,我将不胜感激。

编辑:这是另一个更复杂的示例,它完全正常工作,并且不会显示并发问题:

def complicated(n)
  n.to_s(36).to_a.pack("m").strip * 100
end

items = (1..100_000).to_a

threads = []
10_000.times do |thread|
  threads << Thread.new do
    while item = items.pop

      sleep(rand(100) / 1000.0)
      File.open("test3.txt", "a") { |f|
        f << "#{item} --- #{complicated(item)}\n"
      }

    end
  end
end
threads.each { |t| t.join }
1个回答

3

我也没能够产生错误。

你可能遇到了文件锁。如果你希望多个线程写入同一个文件,它们应该都使用相同的文件对象,像这样:

File.open("test.txt", "a") do |fp|
  threads = []
  
  500.times do |time|
    threads << Thread.new do
      fp.puts("#{time}: 1")
      sleep(rand(100) / 100.0)
      fp.puts("#{time}: 2")
    end
  end
  
  threads.each(&:join)
end

GIL可能会在这个例子中避免任何真正的线程错误,但我不确定在使用真实线程的JRuby下会发生什么情况,两个写操作可能会在完全相同的时间发生。其他采用真实多线程的Ruby引擎也是如此。

关于您是否应该使用锁来保护代码,取决于您想要依赖于您正在使用的Ruby引擎进行保存,还是您想编写一个“应该”适用于所有Ruby引擎的解决方案,无论它们是否具有内置功能来避免并发问题。

另一个问题是,如果您的操作系统和/或文件系统通过文件锁定来保护您免受线程错误,那么您的代码是否应该与操作系统和/或文件系统无关,这意味着您不会依赖于文件系统锁来确保您的文件打开和写入由操作系统和/或文件系统正确同步。

我敢说,这似乎是一种很好的做法,您也要在自己的项目中实现锁定,以确保您的代码在别人将其用于任何Ruby引擎,操作系统或文件系统时都能正常工作,即使大多数现代Ruby引擎,操作系统和文件系统都已经内置了这些功能。


听起来不错,但使用锁有什么缺点吗?(性能方面?) - Benedikt B
运行此基准测试[链接](https://gist.github.com/kaspernj/5048466),在我的i7 3740QM CPU笔记本电脑上,使用Mutex和Monitor对10个线程(1百万总锁)进行100,000次锁定的基准测试分别为0.28和0.72秒,在MRI 1.9.3和2.13上,而在JRuby 1.7.3上为5.13秒。由于您可能会使用更少的锁定,因此您的性能损失将是最小的。 - kaspernj
感谢您关注此事。 - Benedikt B
没问题,伙计!玩线程和锁相关的问题总是很有趣的! :-) - kaspernj
请注意,Thread#join 需要在 File::open 块内部。请参考此问题的答案以了解为什么以及 JRuby 的结果:https://stackoverflow.com/questions/64352404/writing-to-a-file-from-multiple-threads/ - byteit101
@byteit101 谢谢。我已经采纳了你的建议并更新了我的答案 :-) - kaspernj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接