Reputation: 9745
I am trying to find a way to lock writing to a file in different threads. What is the correct way to do it? I would like not to open and close the file all the time when I need to write something, because of the performance.
The code I tried is:
use std::{fs, mem, thread};
use std::os::unix::prelude::FileExt;
use std::sync::{Arc, Mutex};
const COUNT: usize = 1000;
fn main() {
let file: fs::File = fs::File::create("foo.txt").unwrap();
let v: [u32; COUNT] = [6; COUNT];
let counter = Arc::new(Mutex::new(0));
let mut threads = Vec::new();
for _ in 0..COUNT {
let counter = Arc::clone(&counter);
let thread = thread::spawn(move || {
let mut i = counter.lock().unwrap();
let offset = (mem::size_of::<u32>() * (*i)) as u64;
let bytes = unsafe { mem::transmute::<u32, [u8; 4]>(v[*i]) };
file.write_all_at(&bytes, offset).unwrap();
*i += 1;
});
threads.push(thread);
}
for thread in threads {
thread.join().unwrap();
}
}
And it gives the error:
move occurs because `file` has type `std::fs::File`, which does not implement the `Copy` trait
As far as I understand, the file object cannot be easily shared between the threads. I guess, I do something wrong in the whole approach regarding this issue. So what is the correct way to write into a file from different threads in Rust?
Upvotes: 6
Views: 4586
Reputation: 13528
You can wrap the file in an Arc
and a Mutex
, which will allow it to be shared between threads, and mutated by one thread at a time:
fn main() {
let file = Arc::new(Mutex::new(File::create("foo.txt").unwrap()));
let v: [u32; COUNT] = [6; COUNT];
let counter = Arc::new(Mutex::new(0));
let mut threads = Vec::new();
for _ in 0..COUNT {
let counter = Arc::clone(&counter);
let file = Arc::clone(&file);
let thread = thread::spawn(move || {
let mut i = counter.lock().unwrap();
let file = file.lock().unwrap();
let offset = (mem::size_of::<u32>() * (*i)) as u64;
let bytes = unsafe { mem::transmute::<u32, [u8; 4]>(v[*i]) };
file.write_all_at(&bytes, offset).unwrap();
*i += 1;
});
threads.push(thread);
}
for thread in threads {
thread.join().unwrap();
}
}
Note that the read and write methods in std::os::unix::fs::FileExt
take an immutable reference to self
. This means that the code above would compile without the Mutex
, because concurrency is managed by the os kernel. However, the result is non-deterministic given that multiple threads might try to write to the file at the same time, which is probably not what you want.
Upvotes: 9