multithreading - 如何以线程安全的方式使用 Rayon 的 par_iter 读取和修改变量?
问题描述
这段代码:
use rayon::prelude::*; // 1.5.0
fn main() {
let mut items = Vec::new();
items.push("hello");
items.push("foo");
items.push("bar");
items.push("ipsum");
let mut counter = 0;
let results = items.par_iter().map(|item| {
// do something time consuming with item
counter += 1;
print!("completed {} items\r", counter);
0
});
}
产生错误:
warning: unused variable: `item`
--> src/main.rs:12:41
|
12 | let results = items.par_iter().map(|item| {
| ^^^^ help: if this is intentional, prefix it with an underscore: `_item`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `results`
--> src/main.rs:12:9
|
12 | let results = items.par_iter().map(|item| {
| ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_results`
error[E0594]: cannot assign to `counter`, as it is a captured variable in a `Fn` closure
--> src/main.rs:14:9
|
14 | counter += 1;
| ^^^^^^^^^^^^ cannot assign
解决方案
Rust 通过从两个不同的线程写入同一个变量来防止你在这里发生数据竞争。你有几个选择如何解决这个问题。这真的取决于具体情况。
- 最简单的是使用
Mutex
forcounter
。这使您可以安全地访问相同的变量。引入Mutex
有耗尽并行迭代器所有加速的风险,因为一切都将通过Mutex
访问获得顺序。map
如果运行时间很大并且锁定了Mutex
短路,这是可以接受的。 - 对于计数器原子类型的特定情况,例如
AtomicI32
运行良好,但它们很难或不可能用于更复杂的类型。 - 与直接聚合单个变量不同,工作可以并行完成多次,然后合并在一起。这就是
reduce
rayon 的 -functions 所做的。每个线程将至少有一个计数器,它们将被合并在一起以产生一个最终结果。
推荐阅读
- c - 为什么这会导致核心转储?
- debugging - TfidfVectorizer 分数存储在数据框中以标记为单个单词
- java - 具有许多参数的 Mockito 存根方法
- python - 如何在 Qt 样式表中更改 QComboBox QAbstractItemView 边框半径
- java - 如何在非分页 API 之上创建聚合器分页 API?
- html - 启用触摸刺激会改变 css
- ruby-on-rails - 葡萄/招摇中的嵌套资源
- javascript - Dropzone JS - 在没有文件的情况下更新和处理 formData(相同的问题不同的情况)
- node.js - 从 GridFS 播放音频文件
- php - 在 Laravel 项目中删除评论时出现 gor 错误消息