从该方法中创建的线程闭包内部访问未死亡的自我

Accessing non-dying self from inside thread closure created in that method

提问人:mike rodent 提问时间:10/30/2023 更新时间:10/30/2023 访问量:36

问:

基本上,这与这里的问题相同。但我正试图为我的情况制定一个更合适的解决方案。

这是一个 MRE。唯一的依赖项是thread-pool = "*"

use thread_pool::*;

fn main() {
    let framework = HandlingFramework{
        name: "Handler".to_string(),
    };
    framework.do_parallel_things()
}

struct HandlingFramework {
    name: String,
}

impl HandlingFramework {
    fn do_parallel_things(&self) {
        let n_virtual_cores = std::thread::available_parallelism().unwrap().get();
        let (thread_sender, threadpool) = ThreadPool::fixed_size(n_virtual_cores);
        for n in 1..10 {
            println!("iteration {n}");
            let _ = thread_sender.send(move || {
                // inside this thread closure I want to create a useful structure
                // each time which is able to reference properties of the
                // handling framework
                println!("self.name {}", self.name)
                // println!("some alternative message making the program runnable")
            });
        }
        threadpool.shutdown();
        threadpool.await_termination();
    }
}

这给出了描述的编译错误,即

error[E0521]: borrowed data escapes outside of method
  --> src\main.rs:21:15
   |
15 |       fn do_parallel_things(&self) {
   |                             -----
   |                             |
   |                             `self` is a reference that is only valid in the method body
   |                             let's call the lifetime of this reference `'1`
...
21 |               let _ = thread_sender.send(move || {
   |  _____________________^
22 | |                 // inside this thread closure I want to create a structure each time which is able to reference properties of the
23 | |                 // handling framework
24 | |                 println!("self.name {}", self.name)
25 | |             });
   | |              ^
   | |              |
   | |______________`self` escapes the method body here
   |                argument requires that `'1` must outlive `'static`

但。。。我能看到的,毫无疑问,你可以看到的是编译器看不到的:因为线程池在方法结束之前等待所有生成的线程终止,事实上,在实践中的生存期永远不会成为问题。self

&self也是一个不可变的引用。因此,我可以看到,事实上,在这些闭包中引用框架的有用值永远不会成为问题。

我一直在试图找出最简单的解决方案,希望避免使用 ,但不一定*。难道不能通过对一个或多个变量使用特定的生存期设置来解决这个问题吗?在我的 Rust 新手主义水平上,我正在与一生作斗争。Arc<Mutex<...

如果做不到这一点,那么使用呢?那感觉像是失败。而且不会删除所有编译器保护,而不仅仅是您需要删除的保护?unsafeunsafe


* 事实上,我不完全确定如何实现它。但我怀疑,正如我所说,对于这样的案例来说,这太费工程费了。我很可能错了。

多线程 Rust 并行处理 闭包

评论

1赞 Jmb 10/30/2023
只需使用作用域内线程池而不是普通的 .thread-pool
0赞 Jmb 10/30/2023
几乎重复:stackoverflow.com/a/32751956/5397009
0赞 mike rodent 10/30/2023
@Jmb谢谢。我刚刚尝试了scoped-thread-pool,用.这导致了同样的错误。您有机会展示作用域内线程池解决方案的工作原理吗?sendthreadpool.spawn
0赞 Chayim Friedman 10/30/2023
spawn()是针对这种情况的。您需要在其上使用 scoped()execute()。'static
0赞 mike rodent 10/30/2023
哦,哇,明白了(范围解决方案)。匪夷所思。

答:

1赞 mike rodent 10/30/2023 #1

非常感谢 Jmb 和 ChayimFriedman。

use scoped_thread_pool::*;

fn main() {
    let framework = HandlingFramework{
        name: "Handler".to_string(),
    };
    framework.do_parallel_things()
}

struct HandlingFramework {
    name: String,
}
    
impl HandlingFramework {
    fn do_parallel_things(&self) {
        let n_virtual_cores = std::thread::available_parallelism().unwrap().get();
        let threadpool = Pool::new(n_virtual_cores);
        let _ = threadpool.scoped(|scope| {
            for n in 1..10 {
                println!("iteration {n}");
                scope.execute(move ||{
                    println!("self.name {} iteration {n}", self.name)
                })
            }
        });
    }
}

这种“有范围”的东西是某种深奥的魔法。将调查正在发生的事情。

事实上,即使没有.来自文档:“仅当调度程序函数和在给定位置排队的所有作业都已运行时才会返回”。threadpool.shutdown()scopedScope

评论

1赞 Jmb 10/30/2023
你需要把循环放在闭包里,因为线程池会等待线程完成,然后再返回,所以你当前的代码一次只运行一个线程!for n in 1..10scopedscoped
0赞 mike rodent 10/30/2023
有用的知道!编辑。