在博客文章中,作者表明 Rust 不允许通过引用捕获变量:
use std::thread;
use std::sync::atomic::{AtomicI32, Ordering};
struct ThreadSafeCounter {
count: AtomicI32,
}
impl ThreadSafeCounter {
fn increment(&mut self) { self.count.fetch_add(1, Ordering::SeqCst); }
}
pub fn main() {
let n = 10;
let mut counter = ThreadSafeCounter { count: AtomicI32::new(0) };
let mut threads = Vec::new();
for _ in 0..n {
threads.push(thread::spawn( || {
// Rust won't allow this. We are attempting to mutably borrow
// the same value multiple times.
counter.increment();
}));
}
for thread in threads { thread.join(); }
println!("{}", counter.count.load(Ordering::SeqCst));
}
然后对代码进行一处更改,并说道:
Rust 对线程安全的回答是允许对不可变引用进行修改。Rust 将此称为“内部可变性”。只需进行一处小改动,前面的示例即可编译并按预期运行:
use std::thread;
use std::sync::atomic::{AtomicI32, Ordering};
struct ThreadSafeCounter {
count: AtomicI32,
}
impl ThreadSafeCounter {
// increment() uses "interior mutability": it accepts an immutable
// reference, but ultimately mutates the value.
fn increment(&self) { self.count.fetch_add(1, Ordering::SeqCst); }
}
pub fn main() {
let n = 10;
let mut counter = ThreadSafeCounter { count: AtomicI32::new(0) };
let mut threads = Vec::new();
for _ in 0..n {
threads.push(thread::spawn( || {
counter.increment();
}));
}
for thread in threads { thread.join(); }
println!("{}", counter.count.load(Ordering::SeqCst));
}
我收到的错误是:
要强制闭包取得所有权
counter
(以及任何其他引用的变量),请使用move
关键字:
这应该编译吗?该帖子写于 2021 年 12 月 18 日。
最佳答案
1
counter
这篇博文是不正确的。由于生命周期:存在于 的堆栈中,因此该代码无法在任何 Rust 版本或版本(2015、2018 或 2021)下编译,main
因此它最终会超出范围。生成的线程在执行时需要访问。这是由的参数'static
的边界强制执行的。借用检查器的静态分析无法看到线程counter
在返回之前完成使用并连接main
。
使其编译的最直接的方法是将计数器的move
新引用放入每个生成的线程中。
use std::thread;
use std::sync::{atomic::{AtomicI32, Ordering}, Arc};
struct ThreadSafeCounter {
count: AtomicI32,
}
impl ThreadSafeCounter {
// increment() uses "interior mutability": it accepts an immutable
// reference, but ultimately mutates the value.
fn increment(&self) { self.count.fetch_add(1, Ordering::SeqCst); }
}
pub fn main() {
let n = 10;
let mut counter = Arc::new(ThreadSafeCounter { count: AtomicI32::new(0) });
let mut threads = Vec::new();
for _ in 0..n {
let counter = Arc::clone(&counter);
threads.push(thread::spawn(move || {
counter.increment();
}));
}
for thread in threads { thread.join(); }
println!("{}", counter.count.load(Ordering::SeqCst));
}
另一种编译方法是使用。这是标准库的最新成员,它们允许通过强制连接所有生成的线程然后再返回给调用者,从调用者的堆栈中借用变量。
let counter = ThreadSafeCounter { count: AtomicI32::new(0) };
thread::scope(|scope| {
for _ in 0..n {
scope.spawn( || {
counter.increment();
});
}
});
println!("{}", counter.count.load(Ordering::SeqCst));
在编写线程代码时,您更有可能看到普通线程交换消息或共享引用计数数据,而不是看到作用域线程借用数据。这是因为普通线程更灵活并且支持线程池。
|
|