代码之家  ›  专栏  ›  技术社区  ›  middleStackoverflower

使用arcmutex处理线程的正确方法是什么?[副本]

  •  -1
  • middleStackoverflower  · 技术社区  · 11 月前

    我有一些url,然后用块分割它并同时迭代它们,使用克隆将结果保存在vec中:

    pub fn collect_parm_list(url_list: Vec<String>) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let shared_id_list = Arc::new(Mutex::new(vec![]));
    for chunk in url_list.chunks(20) {
        let mut handles = vec![];
        for url in chunk {
            let id_list = Arc::clone(&shared_id_list);
            let handle = thread::spawn(move || {
                // total: optionSale -> result -> total
                let mut id_list_v = id_list.lock().unwrap();
                id_list_v.push(url.to_string())
            });
    
            handles.push(handle);
        }
        // Wait for all threads to complete
        for handle in handles {
            handle.join().unwrap();
        }
    }
    
    let x = shared_id_list.lock().unwrap();
    Ok(x.to_vec())
    }
    

    借用数据有误:

    url_list does not live long enough borrowed value does not live long enough

    at:用于块输入 url_list 大块(20){

    1 回复  |  直到 11 月前
        1
  •  1
  •   cdhowie    11 月前

    你的问题是 .chunks() 不返回已拥有事物的迭代器,它返回一个切片。在这种情况下,这意味着当你有一个 Vec<String> , .chunks() 给你一个迭代器 &[String] ,这意味着 url 是a &String 。然后,您尝试将其关闭 thread::spawn ,但该函数的闭包必须为 'static 这意味着他们不向任何当地人借款。这是错误的来源:传递给的闭包 线程::spawn 间接借款 url_list .

    (旁白:诚然,编译器在引导你到 线程::spawn 调用是问题的根源。 argument requires that "url_list" is borrowed for "'static" 表示它确实知道问题是争论的焦点 线程::spawn 但我不知道为什么它没有明确地突出显示该调用。在我看来,这似乎是编译器诊断中的一个错误。)

    请注意,即使您连接了所有线程句柄,这也是有问题的。借款检查器不知道这种情况的发生,也不知道这意味着什么;它只知道关闭 线程::spawn 必须是 '静态 事实并非如此。(即使它知道这一点,线程也会运行 collect_parm_list 在打开结果时可能会感到恐慌 .join() ,这会导致它下降 url_list 然后,在释放bug后,仍在运行的线程就有了用武之地!)

    一个简单的解决方案是添加 let url = url.to_owned(); 在生成线程之前,这将创建一个独立的副本 url 然后闭合件将捕获该信息。

    或者,如果你有办法通知借用检查器,线程不能过期 url_list 即使当前线程出现恐慌 那就安全了。这正是 scoped threads 完成!使用作用域线程可以避免复制 url ,但是 让您消除 Arc 从输出列表中选择图层。(你仍然需要 Mutex 以同步访问。)这也允许您使用 .into_inner() 静音 当线程完成时,因为您知道此时没有线程仍然可以访问,所以您可以在不等待锁的情况下使用它。

    这种方法的代码也变得简单得多:

    pub fn collect_parm_list(url_list: Vec<String>) -> Vec<String> {
        let id_list = Mutex::new(vec![]);
        thread::scope(|s| {
            for chunk in url_list.chunks(20) {
                for url in chunk {
                    s.spawn(|| {
                        // total: optionSale -> result -> total
                        let mut id_list_v = id_list.lock().unwrap();
                        id_list_v.push(url.to_string())
                    });
                }
            }
        });
    
        id_list.into_inner().unwrap()
    }