如何在线程之间共享对AtomicBool的访问权限?

时间:2015-11-25 08:37:26

标签: multithreading rust

我有这个小程序 - 基本上我希望一个线程能够告诉对方通过结构中的共享布尔值来停止。

use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;
use std::sync::atomic::{AtomicBool, Ordering};

struct Test {
    should_stop: AtomicBool,
    running_thread_handles: Vec<JoinHandle<()>>
}

impl Test {
    fn new() -> Test {
        Test { 
            should_stop: AtomicBool::new(false), 
            running_thread_handles: Vec::new() 
        }
    }

    fn stop(&mut self) {
        self.should_stop.store(true, Ordering::Relaxed);
    }

    fn start(&mut self) {
        let handle = thread::spawn(move || {
            loop {
                println!("Looping");
                thread::sleep(Duration::from_millis(500));

                // I want to effectively do this...
                /*
                if self.stop_bool.load(Ordering::Relaxed) {
                    println!("Stopping");
                    break;
                }*/
            }
        });

        self.running_thread_handles.push(handle);
    }
}

impl Drop for Test {
    fn drop(&mut self) {
        self.stop();

        // Here I want to iterate the self.running_thread_handles and
        // make sure they are cleaned up
    }
}

// I expect to see a 4 "Looping" messages and then a "Stopping" message
fn main() {
   let mut test = Test::new();
   test.start();
   thread::sleep(Duration::from_millis(2000));
   test.stop();
}

也许总有一种更好的方法,但我认为这可能是一种了解生命周期的好方法。

我认为我需要的只是一个Arc所以我试过了:

fn start(&mut self) {
    let stop_bool = Arc::new(&self.should_stop).clone();

    let handle = thread::spawn(move || {
        loop {
            println!("Looping");
            thread::sleep(Duration::from_millis(500));

            if stop_bool.load(Ordering::Relaxed) {
                println!("Stopping");
                break;
            }
        }
    });

    self.running_thread_handles.push(handle);
}

这给了我这个错误:

  

错误:由于需求冲突,无法推断借用表达式的适当生命周期

我认为编译器不理解线程的生命周期,因为我所做的就是将句柄存储在一个向量中,所以我需要以某种方式告诉它,但是如何?

如果我声明这样的结构,我会越来越接近它的工作吗?

struct Test<'a> {
    should_stop: AtomicBool,
    running_thread_handles: &'a Vec<JoinHandle<()>>
}

我还有次要的,相关的,麻烦,我不能为我的生活弄清楚如何遍历我的句柄向量并在Drop impl中调用它们上的任何函数。我想这个解决方案是相关的,所以我不会问这个。

2 个答案:

答案 0 :(得分:13)

有两种方法可以在线程之间访问变量:

  • 借用,这需要保证变量的生命周期超过线程的生命周期
  • 共享所有权(通过Arc):

目前标准图书馆不支持借阅,但crossbeam等第三方包装箱提供借阅。对于共享所有权,Arc确实是一种可能性......

...但是你需要仔细考虑你在Arc中的内容:

let stop_bool = Arc::new(&self.should_stop).clone();

在这里,您要从Arc<&'a AtomicBool>创建&'a self,因此您将通过借用的参考共享所有权。我将回到上面的解释:标准库中不支持跨线程借用。

您需要Arc<AtomicBool>才能获得正确的共享所有权,这可以通过更改Test来完成:

struct Test {
    should_stop: Arc<AtomicBool>,
    running_thread_handles: Vec<JoinHandle<()>>
}

然后,克隆它很容易:

let stop_bool = self.should_stop.clone();

答案 1 :(得分:0)

我在这里发布我自己的答案,希望能提供一个清晰的通用示例,因为接受的答案非常特定于原始问题,让我有点不确定如何去做:

use std::{thread, time};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

fn main() {
  let stop = Arc::new(AtomicBool::new(false)); 
  let stop_me = stop.clone();
  thread::spawn(move || {
      let mut i = 0;
      loop {
          i = i + 1;
          if stop_me.load(Ordering::Relaxed) {
              println!("iterations = {}", i);
              break;
          }
          if i % 1000000 == 0 {
              println!("Another million iterations down...");
          }
      }
  });
  let delay = time::Duration::from_millis(100);
  thread::sleep(delay);
  stop.store(true, Ordering::Relaxed);
  println!("All done");
}

Rust 游乐场链接:construct the URL for the image using this technique