我有一个通用的 struct
,其中包含设置和一个额外的变量设置,我想对其进行调整和使用。
对于整数范围内的所有可能值,我想启动一个(范围)线程,并将此变量设置为该值。根据这个值,它们的工作略有不同。
这些线程中的每一个都应该能够读取通用设置结构。
use crossbeam; // 0.7.3
struct Settings {
// ... many fields
}
const MAX_FEASIBLE_SCORE: u8 = 10;
fn example(settings: Settings) {
crossbeam::scope(|scope| {
for score in 0..MAX_FEASIBLE_SCORE {
scope.spawn(|_| {
let work_result = do_cool_computation(&settings, score);
println!("{:?}", work_result);
});
}
})
.unwrap();
}
fn do_cool_computation(_: &Settings, _: u8) {}
这不编译:
error[E0373]: closure may outlive the current function, but it borrows `score`, which is owned by the current function
--> src/lib.rs:12:25
|
10 | crossbeam::scope(|scope| {
| ----- has type `&crossbeam_utils::thread::Scope<'1>`
11 | for score in 0..MAX_FEASIBLE_SCORE {
12 | scope.spawn(|_| {
| ^^^ may outlive borrowed value `score`
13 | let work_result = do_cool_computation(&settings, score);
| ----- `score` is borrowed here
|
note: function requires argument type to outlive `'1`
--> src/lib.rs:12:13
|
12 | / scope.spawn(|_| {
13 | | let work_result = do_cool_computation(&settings, score);
14 | | println!("{:?}", work_result);
15 | | });
| |______________^
help: to force the closure to take ownership of `score` (and any other referenced variables), use the `move` keyword
|
12 | scope.spawn(move |_| {
| ^^^^^^^^
这将使 &settings
无效,因为第一个循环迭代将在 move
闭包中获取 settings
的所有权。
让它工作的唯一简单方法是:
- 将
Settings
结构复制到每个线程中(这在我的实际应用程序中相当昂贵) - 在
settings
周围引入了一个Arc
,这也感觉有点不幸。
有什么办法可以绕过引用计数吗?有没有一种方法可以将 score
move 到内部闭包中,同时仍允许引用 settings
?
最佳答案
是的,可以只将一个或一些变量 move 到闭包中(而不是全部或不 move )。
是的,这可以用来“规避”引用计数。
我在 rayon::scope
的文档中找到了答案事实证明正是关于这个问题:“访问堆栈数据[从范围内的线程范围内]”。该页面还有一个示例,比这个问题中的伪代码更清晰。
事实证明,您可以按如下方式解决此问题:
使用 move
闭包,但通过用引用遮蔽它们来引用外部作用域中的变量,因此通过引用而不是通过值来捕获它们,使用 let settings = &settings
:
```
crossbeam::scope(|scope| {
let settings = &settings; // refer to outer variable by reference
for score in 0..MAX_FEASIBLE_SCORE {
scope.spawn(move |_| {
let work_result = do_cool_computation(settings, score);
println!("{:?}", work_result);
});
}
})
.unwrap();
```
Playground
所以在这里,我们 move 了'all used variables'但是预先把settings
变成了一个引用,所以它被借用了。 (迂腐地:我们“move 引用”,但这正是“借用”的意思。)
还有第二种可能:借用所有变量(不 move 任何东西)。
这种可能性适用于许多情况,但不适用于此处。 (因为在这里,我们必须按值捕获 score
,因为在 for 循环的下一次迭代和传递给 scope.spawn 的闭包期间,它不会存在
会比这个长寿)。
摘自 rayon::scope
的文档
use rayon;
fn main() {
let ok: Vec<i32> = vec![1, 2, 3];
rayon::scope(|s| {
let bad: Vec<i32> = vec![4, 5, 6];
s.spawn(|_| {
// Transfer ownership of `bad` into a local variable (also named `bad`).
// This will force the closure to take ownership of `bad` from the environment.
let bad = bad;
println!("ok: {:?}", ok); // `ok` is only borrowed.
println!("bad: {:?}", bad); // refers to our local variable, above.
});
s.spawn(|_| println!("ok: {:?}", ok)); // we too can borrow `ok`
});
}
关于multithreading - 有没有办法让 Rust 闭包只将一些变量移入其中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58459643/