首页 > 解决方案 > 更改其他线程中的值并断言后者(在“主”线程中)

问题描述

链接到游乐场(或以下代码):https ://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d6a8e4e6b9fa6aa647b2651e58402aa0

我想为库中的代码编写一个测试。方法外的代码main与库具有相同的结构(https://github.com/housleyjk/ws-rs/blob/master/src/lib.rs#L110)。

在某些时候,我想提供一个可以在线程中随时间更改的值。我必须稍后在另一个线程中断言该值。困难的部分是,保存变化值的类型的实例化是在FnMut闭包中构造的。

我试着移动它,我试过RefCell,我试过,Arc但我失败了。有什么建议么?

use std::thread::sleep;
use std::time::Duration;

trait Fly {}

trait Surf {
    fn will_be_called_after_two_seconds(&mut self);
}

struct Roll;

struct FlyingBird;

impl Fly for FlyingBird {}

struct SurfingDog(bool);

impl Surf for SurfingDog {
    fn will_be_called_after_two_seconds(&mut self) {
        self.0 = true;
    }
}

fn closure_boi<F, S>(x: F) where F: FnMut(Roll) -> S, S: Surf {}

fn main() {
    let val = false;

    std::thread::spawn(|| {
        closure_boi(|_| {
            SurfingDog(val)
        });
    });

    sleep(Duration::from_secs(5));

    assert_eq!(true, val);
}

标签: rust

解决方案


有几个问题可以阻止上述代码工作:

  • 如果 Rust 不能证明使用它的线程不会超过值本身(在这种情况下是一个范围 in main),那么 Rust 将不允许您在两个线程之间共享一个值。解决方法是堆分配值并使用Arc来跟踪它或使用创建线程crossbeam_utils::thread::scope,这确实提供了这样的保证。由于您可能需要生成的线程来保持并行运行,所以让我们使用Arc.
  • SurfingDog不能只包含布尔值,它必须包含对布尔值的引用,或者整个SurfingDog实例必须在main. 在当前代码中,其他线程修改的布尔值位于从它接收的闭包中获得的SurfingDog值中,该值与签入的值不同。closure_boivalmain
  • Rust 不允许其他线程在没有同步的情况下修改第一个线程正在读取的值,因为这会构成数据竞争。解决方法是使用类似锁Mutex或类似AtomicBool.

考虑到上述情况,这里是编译和工作的代码的修改(操场):

use std::sync::{Arc, Mutex};
use std::thread::sleep;
use std::time::Duration;

trait Surf {
    fn will_be_called_after_two_seconds(&mut self);
}

struct SurfingDog<'a>(&'a Mutex<bool>);

impl<'a> Surf for SurfingDog<'a> {
    fn will_be_called_after_two_seconds(&mut self) {
        let mut state = self.0.lock().unwrap();
        *state = true;
    }
}

fn closure_boi<S: Surf>(mut x: impl FnMut() -> S) {
    let mut surfer = x();
    sleep(Duration::from_secs(2));
    surfer.will_be_called_after_two_seconds();
    sleep(Duration::from_secs(10)); // keep running
}

fn main() {
    let val = Arc::new(Mutex::new(false));
    std::thread::spawn({
        let val = Arc::clone(&val);
        move || {
            closure_boi(|| SurfingDog(&*val));
        }
    });
    sleep(Duration::from_secs(5));
    assert_eq!(true, *val.lock().unwrap());
}

推荐阅读