如何从人造丝`par_iter`中写入文件?

问题描述 投票:0回答:1

“我是 Rust 的新手,我正在做一个项目,我需要扫描大量文件夹中的文件并将过滤后的数据保存到 JSON 文件中。我目前正在使用 Rayon 执行快速 foreach 循环在包含文件夹的“Vec”上。在循环中,我读取一个文件,过滤有用的信息并将其保存到一个文件中。

这是最终的working版本。但是我怀疑这不是最好的解决方案。

fn main() {
    // ...

    // Imagine this is full of data
    let mut folder_nas: Vec<FolderNAS> = Vec::new();

    // Open a out.json file to store the results in append mode
    let mut file = OpenOptions::new()
        .write(true)
        .append(true)
        .open(FILENAME)
        .unwrap();
    file.write_all("Some data").unwrap();

    folder_nas.par_iter().for_each(|x| {
        let mut file_iterator = OpenOptions::new()
            .write(true)
            .append(true)
            .open(FILENAME)
            .unwrap();
        file_iterator
            .write_all("Some filtered data")
            .unwrap();
    });
    file.write_all("Some data").unwrap();
}

起初,来自其他语言我试过这个。

fn main() {
    // ...

    // Imagine this is full of data
    let mut folder_nas: Vec<FolderNAS> = Vec::new();

    // Open a out.json file to store the results in append mode
    let mut file = OpenOptions::new()
        .write(true)
        .append(true)
        .open(FILENAME)
        .unwrap();
    file.write_all("Some data").unwrap();

    folder_nas.par_iter().for_each(|x| {
        // Notice the name difference
        file.write_all("Some filtered data")
            .unwrap();
    });
    file.write_all("Some data").unwrap();
}

这种方法最终给了我一个错误,因为

file
变量用于
for_each
和更高版本。我的解决方案是在
OpenOptions
中打开一个新的
for_each
编写器。但我的问题是,我怎么能使用
file
变量而不创建新的作者?

rust borrow-checker
1个回答
0
投票

正如 Chayim Friedman 在评论中所展示的那样,您并不真的需要为了写入它而改变

File
。这是因为
&File
实现了
Write
,反映了从多个线程写入操作系统级文件句柄完全没问题的事实。但是,这种方法有两个问题:

  • 不保证
    File::write_all()
    可以一次写完所有内容。如果底层
    File::write()
    表示只写入了一部分数据,它会发出一个新的
    write()
    来写出其余部分。这个
    write()
    可能在另一个线程发出它自己的
    write()
    s 之后出现,导致文件中的数据交错(损坏)。
  • 如果您决定将
    File
    包装到
    BufWriter
    中,那么该技巧将不起作用,如果您要写出大量数据,这是一个非常合理的做法。

因此我建议只使用 Mutex,它解决了这两个问题(这也是像 C 和 C++ 这样的语言在幕后所做的):

let mut file = OpenOptions::new()
    .write(true)
    .append(true)
    .open("out.json")
    .unwrap();
file.write_all(b"Some data").unwrap();

// wrap file in a Mutex to use it in parallel
let file = Mutex::new(file);
folder_nas.par_iter().for_each(|_x| {
    // lock the mutex to access the file
    file.lock().unwrap().write_all(b"Some filtered data").unwrap();
});
// extract the original file and use it as before
let mut file = file.into_inner().unwrap();
file.write_all(b"Some data").unwrap();

游乐场

© www.soinside.com 2019 - 2024. All rights reserved.