Ruby MRI 1.8.7 - 文件编写线程安全性

时间:2013-02-27 09:26:29

标签: ruby multithreading file concurrency

在我看来,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 个答案:

答案 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引擎,操作系统和文件系统都内置了这些功能,其他人也会使用您的代码。