我有一个带有设置的通用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
移到内部闭包中,同时仍然允许引用settings
?
2条答案
按热度按时间oogrdqng1#
是的,可以只将一个或一些变量移到闭包中(而不是全部或一个都不移)。
是的,这可以用来“规避”引用计数。
我在
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所以在这里,我们移动了'all used variables',但事先将
settings
转换为引用,所以它是借用的。(教育性地:我们“移动引用”,但这正是“借用”的含义。)还有第二种可能性:借用所有变量(不移动任何内容)。
这种可能性在很多上下文中都有效,但在这里不行(因为在这里,我们 * 必须 * 通过值来捕获
score
,因为在for循环的下一次迭代中它将不在那里,传递给scope.spawn
的闭包将比它更有效)。摘自
rayon::scope
的文档Playground
eqoofvh92#
closure! macro from the closure crate提供了选择性地引用、移动或克隆变量到闭包中的能力。
示例摘自文档:
已捕获但未列出的变量默认为被移动。