在我看来,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 }
答案 0 :(得分:3)
我也无法产生错误。
你可能在这里遇到了文件锁。如果您希望多个线程写入同一个文件,它们应该使用相同的文件对象,如下所示:
threads = []
File.open("test.txt", "a") do |fp|
500.times do |time|
threads << Thread.new do
fp.puts("#{time}: 1")
sleep(rand(100) / 100.0)
fp.puts("#{time}: 2")
end
end
end
threads.each{ |thread| thread.join }
在这个例子中,GIL可能会让你免于任何真正的线程错误,但我不确定,在JRuby下会发生什么,它使用真正的线程并且两次写入可能在同一时间发生。其他具有真实线程的Ruby引擎也是如此。
关于你应该用锁保护你的代码的地方的问题归结为,如果你想依赖你正在使用的Ruby引擎应该保存你,或者你想编写一个“应该”适用于所有的解决方案Ruby引擎,无论它们是否具有内置功能,都可以避免并发问题。
另一个问题是,如果您的操作系统和/或文件系统正在通过文件锁保存您的线程错误,并且您的代码应该是操作系统和/或文件系统独立的,这意味着您不会依赖在文件系统锁上,以确保操作系统和/或文件系统正确地同步文件打开和写入。
我会说,这似乎是一种好习惯,你也可以在你身边实现锁定,以确保你的代码能够继续工作,无论哪个Ruby引擎,操作系统或文件系统即使大多数现代Ruby引擎,操作系统和文件系统都内置了这些功能,其他人也会使用您的代码。