关于Rust中的借用参考文献的问题

hjqgdpho  于 2022-11-12  发布在  其他
关注(0)|答案(4)|浏览(179)

我正在尝试编写一个程序,其中一个线程写入队列,另一个线程从队列中读取
但是我遇到了一个关于在读取队列的线程中访问“队列”的问题
下面是未编译的代码

use ::std::collections::VecDeque;
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::thread;
use std::time::Duration;

fn main() {
    //let path = std::env::args()
    //   .nth(1)
    //   .expect("Argument 1 needs to be a path");
    //println!("watching {}", path);
    let path = "c:\\testfolder";

    if let Err(e) = watch(path) {
        println!("error: {:?}", e)
    }
}

fn process_queue(queue: &VecDeque<String>) -> () {}

fn watch<P: AsRef<Path>>(path: P) -> notify::Result<()> {
    let (tx, rx) = std::sync::mpsc::channel();
    // Automatically select the best implementation for your platform.
    // You can also access each implementation directly e.g. INotifyWatcher.
    let mut watcher = RecommendedWatcher::new(tx, Config::default())?;

    // Add a path to be watched. All files and directories at that path and
    // below will be monitored for changes.
    let mut queue: VecDeque<String> = VecDeque::new();

    thread::spawn(|| {
        // everything in here runs
        process_queue(&queue)
    });

    watcher.watch(path.as_ref(), RecursiveMode::Recursive)?;

    for res in rx {
        match res {
            Ok(event) => {
                println!("changed: {:?}", event.paths);
                let os_str: String = String::from(event.paths[0].to_str().unwrap());
                //let my_str: String = os_str.unwrap().to_str().unwrap();
                //let s =os_str.into_os_string();

                queue.push_back(os_str);
            }
            Err(e) => println!("watch error: {:?}", e),
        }
    }

    Ok(())
}

Rust编译器的输出

error[E0373]: closure may outlive the current function, but it borrows `queue`, which is owned by the current function
  --> src\main.rs:43:19
   |
43 |     thread::spawn(|| {
   |                   ^^ may outlive borrowed value `queue`
...
47 |         process_queue(&queue)
   |                        ----- `queue` is borrowed here
   |
note: function requires argument type to outlive `'static`
  --> src\main.rs:43:5
   |
43 | /     thread::spawn(|| {
44 | |
45 | |         // everything in here runs
46 | |
47 | |         process_queue(&queue)
48 | |
49 | |     });
   | |______^
help: to force the closure to take ownership of `queue` (and any other referenced variables), use the `move` keyword
   |
43 |     thread::spawn(move || {
   |                   ++++

error[E0502]: cannot borrow `queue` as mutable because it is also borrowed as immutable
  --> src\main.rs:63:17
   |
43 |       thread::spawn(|| {
   |       -             -- immutable borrow occurs here
   |  _____|
   | |
44 | |
45 | |         // everything in here runs
46 | |
47 | |         process_queue(&queue)
   | |                        ----- first borrow occurs due to use of `queue` in closure
48 | |
49 | |     });
   | |______- argument requires that `queue` is borrowed for `'static`
...
63 |                   queue.push_back(os_str);
   |                   ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

从错误中我了解到编译器不允许同时使用可变和不可变引用。
但我不知道如何实现我试图用这些限制做的事情。

sbtkgmzw

sbtkgmzw1#

解决这个问题的一种方法是Box-ing VecDeque,这样就可以将一个克隆的引用传递给process_queue函数。
使用Box允许您在堆上分配VecDeque,以便您可以为派生的线程提供对Vec的引用,并且仍然可以在当前线程中改变队列。
这看起来像:

let mut queue = Box::new(VecDeque::new());
let queue_clone = queue.clone();

thread::spawn(|| {
    // queue_clone is now moved into the fn closure and is 
    // not accessible to the code below
    process_queue(queue_clone)
});

并且可以更新process_queue以接受正确的类型:

fn process_queue(queue: Box<VecDeque<String>>) -> () { }

请注意,使用这种实现,process_queue只在线程产生时运行一次,如果您希望process_queue在每次队列更改时执行某些操作,则遵循其他人的建议,使用类似Channels这样的语句是最有意义的。

icnyk63a

icnyk63a2#

感谢您的所有回复
从所有的响应中,我了解到使用通道并将接收器循环移动到其他线程(如user4815162342所建议的)将是最好的解决方案

332nm8kg

332nm8kg3#

根据您的建议,我使用渠道成功地实现了我所要做的事情。
最终工作代码粘贴在下面

use std::thread;
use std::time::Duration;
use notify::{RecommendedWatcher, RecursiveMode, Watcher, Config};
use std::path::Path;
use std::path::PathBuf;

//
fn main() {

    //let path = std::env::args()
     //   .nth(1)
     //   .expect("Argument 1 needs to be a path");
    //println!("watching {}", path);
    let path="c:\\testfolder";    

    if let Err(e) = watch(path) {
        println!("error: {:?}", e)
    }

}

fn watch<P: AsRef<Path>>(path: P) -> notify::Result<()> {
    let (tx, rx) = std::sync::mpsc::channel();      

    // Automatically select the best implementation for your platform.
    // You can also access each implementation directly e.g. INotifyWatcher.
    let mut watcher = RecommendedWatcher::new(tx, Config::default())?;

    // Add a path to be watched. All files and directories at that path and
    // below will be monitored for changes.

    let handle=thread::spawn(move || {

        // everything in here runs        

        for res in rx {
            match res {
                Ok(event) =>{

                   // println!("changed: {:?}", event.paths); 
                    let os_str:String = String::from(event.paths[0].to_str().unwrap());
                    println!("file name: {}", os_str);               

                },
                Err(e) => println!("watch error: {:?}", e),
            }
        }

    }); 

    watcher.watch(path.as_ref(), RecursiveMode::Recursive)?;    
    handle.join();  

    Ok(())
}
bt1cpqcv

bt1cpqcv4#

在您的情况下,使用Rust的MPSC(多生产者单消费者,即本质上是一个队列)可能是最好的。您也可以使用ArcMutex结构体创建一个在多个线程之间共享的变量,但这会过度使用,并可能会影响性能(任何时候只有一个线程可以访问该变量)。
下面是一个多线程MPSC的示例,我将让您根据您的基础架构对其进行调整:

use std::{sync::mpsc, thread};

fn main() {
    let (sender, receiver) = mpsc::channel();

    let handle_1 = thread::spawn(|| {
        thread_1(sender);
    });
    let handle_2 = thread::spawn(|| {
        thread_2(receiver);
    });

    handle_1.join().unwrap();
    handle_2.join().unwrap();
}

// the enum must have the Send trait (automatically implemented)
enum Instruction {
    Print(String),
    Exit
}

fn thread_1(sender: mpsc::Sender<Instruction>) {
    sender.send(Instruction::Print("I".to_owned())).unwrap();
    sender.send(Instruction::Print("like".to_owned())).unwrap();
    sender.send(Instruction::Print("Rust".to_owned())).unwrap();
    sender.send(Instruction::Print(".".to_owned())).unwrap();
    sender.send(Instruction::Exit).unwrap();
}

fn thread_2(receiver: mpsc::Receiver<Instruction>) {
    'global_loop: loop {
        for received in receiver.recv() {
            match received {
                Instruction::Print(string) => print!("{} ", string),
                Instruction::Exit => {
                    println!("");
                    break 'global_loop;
                }
            }
        }
    }
}

相关问题