Multithreading 在多个线程中变异共享可变变量

Multithreading 在多个线程中变异共享可变变量,multithreading,rust,Multithreading,Rust,我想在多个线程中更改一个可变变量。我知道这不是线程安全的,但我想知道Rust编译器将如何处理它。因此,我使用range的map函数生成子线程: use std::thread; fn break_law(value: &mut i32) { *value += 20; } fn main() { let mut x = 10; let handles = (0..10).map(|| { thread::spawn(move || {

我想在多个线程中更改一个可变变量。我知道这不是线程安全的,但我想知道Rust编译器将如何处理它。因此,我使用range的
map
函数生成子线程:

use std::thread;

fn break_law(value: &mut i32) {
    *value += 20;
}

fn main() {
    let mut x = 10;

    let handles = (0..10).map(|| {
        thread::spawn(move || {
        break_law(&mut x);
        println!("{:?}", x);
        })
    }).collect();

    for h in handles {
        h.join().unwrap();
    }
}
但我有一个错误:

break_law1.rs:10:24: 15:4 error: type mismatch: the type `[closure@break_law1.rs
:10:28: 15:3 x:_]` implements the trait `core::ops::FnMut<()>`, but the trait `c
ore::ops::FnMut<(_,)>` is required (expected tuple, found ()) [E0281]
break_law1.rs:10        let handles = (0..10).map(|| {
break_law1.rs:11                thread::spawn(move || {
break_law1.rs:12                        break_law(&mut x);
break_law1.rs:13                        println!("{:?}", x);
break_law1.rs:14                })
break_law1.rs:15        }).collect();
break_law1.rs:10:24: 15:4 help: run `rustc --explain E0281` to see a detailed ex
planation
break_law1.rs:10:24: 15:4 error: type mismatch: the type `[closure@break_law1.rs
:10:28: 15:3 x:_]` implements the trait `core::ops::FnOnce<()>`, but the trait `
core::ops::FnOnce<(_,)>` is required (expected tuple, found ()) [E0281]
break_law1.rs:10        let handles = (0..10).map(|| {
break_law1.rs:11                thread::spawn(move || {
break_law1.rs:12                        break_law(&mut x);
break_law1.rs:13                        println!("{:?}", x);
break_law1.rs:14                })
break_law1.rs:15        }).collect();
break_law1.rs:10:24: 15:4 help: run `rustc --explain E0281` to see a detailed ex
planation
break_law1.rs:15:5: 15:14 error: type mismatch: the type `[closure@break_law1.rs
:10:28: 15:3 x:_]` implements the trait `core::ops::FnMut<()>`, but the trait `c
ore::ops::FnMut<(_,)>` is required (expected tuple, found ()) [E0281]
break_law1.rs:15        }).collect();
                           ^~~~~~~~~
break_law1.rs:15:5: 15:14 help: run `rustc --explain E0281` to see a detailed ex
planation
break_law1.rs:18:6: 18:14 error: the type of this value must be known in this co
ntext
break_law1.rs:18            h.join().unwrap();
                            ^~~~~~~~
break_law1.rs:17:2: 19:3 note: in this expansion of for loop expansion
error: aborting due to 4 previous errors

这看起来很奇怪,但我该如何纠正?为什么?

map的类型签名表示它接受类型参数(
F
),它是一个闭包(
FnMut
),传递一个参数,该参数是迭代器产生的类型(
(Self::Item)
)并返回一个
B
->B
)。您缺少的部分是参数:它需要是
.map(|x |{…})
而不是
.map(|{…})

如果您不关心参数的值,您可以编写
.map(| |{…})
,使用
\uu
模式忽略它,或者编写
.map(| | i{…})
,其中前导的
\u
是一种约定,用于指示某个变量将不使用(它使编译器关于未使用变量的正常警告静音)

FWIW,错误消息有点长,但它确实包含以下信息:

type mismatch: the type `[closure@break_law1.rs:10:28: 15:3 x:_]` implement
    the trait `core::ops::FnMut<()>`, 
but the trait `core::ops::FnMut<(_,)>` is required
类型不匹配:类型不匹配`[closure@break_law1.rs:10:28:15:3 x:"`执行
trait`core::ops::FnMut`,
但是特性'core::ops::FnMut'是必需的

我用换行符来突出区别:编译器抱怨你传递给
map
的闭包不带参数(
FnMut
),而实际上它需要一个带参数的闭包(
FnMut
表示一个类型参数没有足够的信息来完全推断).

顺便说一句,即使在解决了如上所示的参数编号问题之后,您也不会得到想要的行为。代码将成功编译,因为只要Rust中的整型实现了
copy
特性,每个线程将收到变量
x
副本

我认为正确的示例应该如下所示:

// Creates a new iterator that will apply the specified function to each
// element returned by the first, yielding the mapped element instead.
fn map<B, F>(self, f: F) -> Map<Self, F> 
    where F: FnMut(Self::Item) -> B
fn break_law(value: &mut i32) {
    *value += 20;
}

fn main() {
    let mut x = 10;
    let ref_x = &mut x;

    let handles: Vec<_> = (0..10).map(|_| {
        thread::spawn(move || {
            break_law(ref_x);
            println!("{:?}", x);
        })
    }).collect();

    for h in handles {
        h.join().unwrap();
    }
} 
fn违反法则(值:&mut i32){
*数值+=20;
}
fn main(){
设mut x=10;
设ref_x=&mut x;
let句柄:Vec=(0..10).map(| |{
线程::生成(移动| |{
违反法律(参考x);
println!(“{:?}”,x);
})
}).收集();
用于手柄中的h{
h、 join().unwrap();
}
} 

确实有效。谢谢。与C++相比,这个函数签名真的不同。顺便说一句,正如@swizard所说,每个线程都会收到一个变量x的副本,因此外部作用域中的x不会发生变异。这种修改会触发编译错误:
x
的寿命不够长。但是我认为
x
应该比
ref\u x
有更长的生命周期,直到最后一个括号。@AselanDu我认为您看到的编译器错误的问题是,使用
spawn
创建的线程可能会超过它在其中生成的范围。这意味着从它引用的所有内容都必须有一个静态的生存期。由于x将在
main
结束时被删除,
ref_x
不能有
的静态寿命。