如何修改一个线程中的值并使用共享内存读取另一个线程中的值?
How do I modify a value in one thread and read the value in another thread using shared memory?
以下Python代码创建了一个线程(实际上是一个进程),其中包含传递给它的两个浮点数的数组,线程每 5 秒第一个浮点数增加 1,第二个浮点数增加 -1 ,而主线程不断打印两个浮点数:
from multiprocessing import Process, Array
from time import sleep
def target(states):
while True:
states[0] -= 1
states[1] += 1
sleep(5)
def main():
states = Array("d", [0.0, 0.0])
process = Process(target=target, args=(states,))
process.start()
while True:
print(states[0])
print(states[1])
if __name__ == "__main__":
main()
如何在 Rust 中使用共享内存做同样的事情?我已尝试执行以下操作 (playground):
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new([0.0]));
let data = data.clone();
thread::spawn(move || {
let mut data = data.lock().unwrap();
data[0] = 1.0;
});
print!("{}", data[0]);
}
但这会导致编译错误:
error: cannot index a value of type `std::sync::Arc<std::sync::Mutex<[_; 1]>>`
--> <anon>:12:18
|>
12 |> print!("{}", data[0]);
|> ^^^^^^^
即使那行得通,它也会做一些不同的事情。我已经阅读了 this,但我仍然不知道该怎么做。
好的,让我们首先修复编译器错误:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new([0.0]));
let thread_data = data.clone();
thread::spawn(move || {
let mut data = thread_data.lock().unwrap();
data[0] = 1.0;
});
println!("{}", data.lock().unwrap()[0]);
}
变量thread_data
总是被移动到线程中,这就是为什么它不能在线程产生后被访问。
但这仍然有一个问题:您正在启动一个线程,该线程将 运行 与主线程并发,并且最后一个打印语句将在线程大多数时间更改值之前执行(它将是随机的)。
要解决此问题,您必须在打印值之前等待线程完成:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new([0.0]));
let thread_data = data.clone();
let t = thread::spawn(move || {
let mut data = thread_data.lock().unwrap();
data[0] = 1.0;
});
t.join().unwrap();
println!("{}", data.lock().unwrap()[0]);
}
这将始终产生正确的结果。
您的代码离您不远了! :)
让我们先看看编译器错误:它说您显然正在尝试索引某些东西。没错,你想索引data
变量(带data[0]
),但是编译器报错说你要索引的值是std::sync::Arc<std::sync::Mutex<[_; 1]>>
类型,无法索引。
如果您查看类型,您很快就会发现:我的数组仍然包裹在 Mutex<T>
中,而 Mutex<T>
又包裹在 Arc<T>
中。这给我们带来了解决方案:您也必须锁定读取权限。所以你必须像在另一个线程中一样添加 lock().unwrap()
:
print!("{}", data.lock().unwrap()[0]);
但是现在出现了一个新的编译器错误:use of moved value: `data`
。该死!这来自你的名字阴影。你在开始线程之前说 let data = data.clone();
;这遮盖了原来的 data
。那么我们用 let data_for_thread = data.clone()
替换它并在另一个线程中使用 data_for_thread
怎么样?你可以看到工作结果 here on the playground.
让它做与 Python 示例相同的事情不再那么难了,是吗?
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
let data = Arc::new(Mutex::new([0.0, 0.0]));
let data_for_thread = data.clone();
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(5))
let mut data = data_for_thread.lock().unwrap();
data[0] += 1.0;
data[1] -= 1.0;
}
});
loop {
let data = data.lock().unwrap();
println!("{}, {}", data[0], data[1]);
}
你可以试试 here on the playground,尽管我更改了一些小东西以允许 运行 在操场上。
如果您通过一个线程更新公共数据,其他线程可能看不到更新后的值,除非您执行以下操作:
将变量声明为 volatile 以确保将最新更新返回给读取该变量的线程。数据是从内存块中读取的,而不是从缓存中读取的。
使所有更新和读取同步,这在性能方面可能代价高昂,但由于写入和读取的非同步方法,肯定会处理数据 corruptions/in-consistency通过不同的线程。
以下Python代码创建了一个线程(实际上是一个进程),其中包含传递给它的两个浮点数的数组,线程每 5 秒第一个浮点数增加 1,第二个浮点数增加 -1 ,而主线程不断打印两个浮点数:
from multiprocessing import Process, Array
from time import sleep
def target(states):
while True:
states[0] -= 1
states[1] += 1
sleep(5)
def main():
states = Array("d", [0.0, 0.0])
process = Process(target=target, args=(states,))
process.start()
while True:
print(states[0])
print(states[1])
if __name__ == "__main__":
main()
如何在 Rust 中使用共享内存做同样的事情?我已尝试执行以下操作 (playground):
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new([0.0]));
let data = data.clone();
thread::spawn(move || {
let mut data = data.lock().unwrap();
data[0] = 1.0;
});
print!("{}", data[0]);
}
但这会导致编译错误:
error: cannot index a value of type `std::sync::Arc<std::sync::Mutex<[_; 1]>>`
--> <anon>:12:18
|>
12 |> print!("{}", data[0]);
|> ^^^^^^^
即使那行得通,它也会做一些不同的事情。我已经阅读了 this,但我仍然不知道该怎么做。
好的,让我们首先修复编译器错误:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new([0.0]));
let thread_data = data.clone();
thread::spawn(move || {
let mut data = thread_data.lock().unwrap();
data[0] = 1.0;
});
println!("{}", data.lock().unwrap()[0]);
}
变量thread_data
总是被移动到线程中,这就是为什么它不能在线程产生后被访问。
但这仍然有一个问题:您正在启动一个线程,该线程将 运行 与主线程并发,并且最后一个打印语句将在线程大多数时间更改值之前执行(它将是随机的)。
要解决此问题,您必须在打印值之前等待线程完成:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new([0.0]));
let thread_data = data.clone();
let t = thread::spawn(move || {
let mut data = thread_data.lock().unwrap();
data[0] = 1.0;
});
t.join().unwrap();
println!("{}", data.lock().unwrap()[0]);
}
这将始终产生正确的结果。
您的代码离您不远了! :)
让我们先看看编译器错误:它说您显然正在尝试索引某些东西。没错,你想索引data
变量(带data[0]
),但是编译器报错说你要索引的值是std::sync::Arc<std::sync::Mutex<[_; 1]>>
类型,无法索引。
如果您查看类型,您很快就会发现:我的数组仍然包裹在 Mutex<T>
中,而 Mutex<T>
又包裹在 Arc<T>
中。这给我们带来了解决方案:您也必须锁定读取权限。所以你必须像在另一个线程中一样添加 lock().unwrap()
:
print!("{}", data.lock().unwrap()[0]);
但是现在出现了一个新的编译器错误:use of moved value: `data`
。该死!这来自你的名字阴影。你在开始线程之前说 let data = data.clone();
;这遮盖了原来的 data
。那么我们用 let data_for_thread = data.clone()
替换它并在另一个线程中使用 data_for_thread
怎么样?你可以看到工作结果 here on the playground.
让它做与 Python 示例相同的事情不再那么难了,是吗?
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
let data = Arc::new(Mutex::new([0.0, 0.0]));
let data_for_thread = data.clone();
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(5))
let mut data = data_for_thread.lock().unwrap();
data[0] += 1.0;
data[1] -= 1.0;
}
});
loop {
let data = data.lock().unwrap();
println!("{}, {}", data[0], data[1]);
}
你可以试试 here on the playground,尽管我更改了一些小东西以允许 运行 在操场上。
如果您通过一个线程更新公共数据,其他线程可能看不到更新后的值,除非您执行以下操作:
将变量声明为 volatile 以确保将最新更新返回给读取该变量的线程。数据是从内存块中读取的,而不是从缓存中读取的。
使所有更新和读取同步,这在性能方面可能代价高昂,但由于写入和读取的非同步方法,肯定会处理数据 corruptions/in-consistency通过不同的线程。