【问题标题】:What is wrong with this implementation of Barrier using atomics in Rust?在 Rust 中使用原子的 Barrier 实现有什么问题?
【发布时间】:2022-12-04 03:38:05
【问题描述】:

我已经编写了以下仅使用原子的 Barrier 实现:

use std::sync::atomic::{AtomicUsize, Ordering};

pub struct Barrier {
  pub done: AtomicUsize,
  pub tids: usize,
}

impl Barrier {
  pub fn new(tids: usize) -> Barrier {
    Barrier {
      done: AtomicUsize::new(0),
      tids,
    }
  }

  pub fn wait(&self) {
    let done = self.done.fetch_add(1, Ordering::SeqCst);
    if done + 1 == self.tids {
      self.done.store(0, Ordering::SeqCst);
    } else {
      while self.done.load(Ordering::SeqCst) != 0 {}
    }
  }
}

它没有按预期工作。例如,

// inside threads loop
barrier.wait();
println!("a");
barrier.wait();
println!("b");

直觉上,它应该可以工作,因为一旦调用.wait(),它就会挂在while循环上,在所有线程调用.wait()后摆脱它,并为下一个.wait()重置计数器。相反,它最终会挂起。下面是一个用法示例:

fn main() {
  println!("Hello, world!");

  let barrier = &Barrier::new(10);

  std::thread::scope(|s| {
    for tid in 0 .. 10 {
      s.spawn(move || {
        loop {
          barrier.wait();
          println!("{} a", tid);
          barrier.wait();
          println!("{} b", tid);
        }
      });
    }
  });
}

【问题讨论】:

  • @cafce25 添加了一个用法示例并修复了问题上的问题。
  • 编辑:哦,我现在可以看到问题了。它会挂起,因为在另一个线程从循环中退出之前,一个线程可能会增加下一个 .wait() 的计数器。愚蠢的。不过,我会保留这个问题。

标签: multithreading rust barrier


【解决方案1】:

在 Rust 中使用原子实现 Barrier 有几个问题。

首先,Barrier 结构中的 done 计数器永远不会重置为 0。这意味着一旦 done 计数器达到 tids 的值,它就永远不会被重置,wait() 函数将始终挂在 while 循环中。

其次,当线程从 wait() 函数中释放时,完成计数器不会递减。这意味着完成计数器将不断增加,最终溢出并导致未定义的行为。

第三,完成计数器不受任何同步机制(如互斥体)的保护,因此多个线程可能同时修改完成计数器,从而导致竞争条件和不可预测的行为。

总的来说,Barrier 的这种实现是不正确的,不应该被使用。相反,更好的方法是使用互斥锁来保护完成计数器并在所有线程到达屏障后正确重置它。

【讨论】:

  • 完成计数器确实在 self.done.store(0, Ordering::SeqCst) 行上重置。这是 GPT-3 输出吗?
猜你喜欢
  • 1970-01-01
  • 2014-12-24
  • 2014-08-19
  • 1970-01-01
  • 1970-01-01
  • 2017-09-16
  • 2019-06-07
  • 2021-07-25
  • 2011-09-08
相关资源
最近更新 更多