Ruby MRI 1.8.7 - File Writing Security

It seems to me that writing to Ruby MRI 1.8.7 is completely thread safe.

Example 1 - Flawless Results:

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 } } 

Example 2 - Flawless results (but slower):

 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 } 

So, I was not able to recover the scenario in which I ran into concurrency problems, can you?

I would appreciate it if someone could explain to me why I should still use Mutex here.

EDIT: here is another more complex example that works fine and does not show the concurrency problem:

 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 } 
+4
source share
1 answer

I also could not create a mistake.

You are probably working with file locking. If you want multiple threads to be written to the same file, all of them must use the same file object:

 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 } 

In this example, the GIL will likely save you from any real thread errors, but I'm not sure what will happen in JRuby, which uses real threads, and two entries can happen at the same time. The same goes for other real-thread Ruby engines.

Regarding the issue of wherever you protect your code with locks, it comes down to the fact that if you want to rely on the Ruby engine you use, you must save money or want to encode a solution that "should" work on all Ruby- engine, regardless of whether they have built-in functions, to save you from concurrency problems.

Another question: if your operating system and / or file system saves you from stream errors with file locks, and if your code should be independent of the operating system and / or file system, it means that you are not dependent on file system locks, to ensure that your file openings and records are correctly synchronized by the operating system and / or file system.

I will go to the limb and say that it seems good practice that you also implement locks on your side to make sure your code works no matter which Ruby engine, operating system or file system someone else uses your code although most modern Ruby engines, operating systems, and file systems have built-in features.

+3
source

All Articles