代码之家  ›  专栏  ›  技术社区  ›  Blankman

产生100个线程并增加一个共享计数器,这可以优化/改进到在1秒内完成吗?

  •  0
  • Blankman  · 技术社区  · 2 年前

    我想学习如何在Rust中编写这个Go代码,Go代码在这里供参考: https://go.dev/play/p/j9osOG5xs1R

    它基本上启动100个线程,在每个线程中循环1000次,每次迭代睡眠1毫秒,还增加一些共享状态。

    由于它睡眠时间为1毫秒,因此应该在1秒内完成。

    在我的Go版本中,我实际上创建了100个线程,它在大约1秒内完成,正如预期的那样。

    我的铁锈代码目前只有10个线程,需要10秒才能完成。

    我一定做错了什么,有什么提高表现的建议吗?

    use std::sync::{Arc, Mutex};
    use std::thread;
    use std::time::Duration;
    
    fn main() {
        let counter = Arc::new(Mutex::new(0));
        let mut handles = vec![];
    
        use std::time::Instant;
        let now = Instant::now();
    
        for _ in 0..10 {
            let counter = Arc::clone(&counter);
            let handle = thread::spawn(move || {
    
                let mut num = counter.lock().unwrap();
    
                for _ in 0..1000 {
                    thread::sleep(Duration::from_millis(1));
                    *num += 1;
                }
            });
            handles.push(handle);
        }
    
        for handle in handles {
            handle.join().unwrap();
        }
    
        println!("Result: {}", *counter.lock().unwrap());
    
        let elapsed = now.elapsed();
        println!("Elapsed: {:.2?}", elapsed);
    }
    

    我甚至在发布模式下构建了该程序:

    cargo build --release
    ./target/release/spawner
    

    出于某种原因,它只需要10个线程超过10秒。。。

    1 回复  |  直到 2 年前
        1
  •  7
  •   John Kugelman Michael Hodel    2 年前

    每个线程锁定 counter 在整个1秒内,它运行,阻止所有线程进行任何进展。这导致它们按顺序运行,而不是并行运行。

    如果缩小锁的范围,那么它们将每毫秒获取并释放互斥体,从而解除彼此的锁定,并允许程序在预期的1秒内完成。

    let handle = thread::spawn(move || {
        for _ in 0..1000 {
            thread::sleep(Duration::from_millis(1));
            let mut num = counter.lock().unwrap();
            *num += 1;
        }
    });
    

    输出

    Result: 10000
    Elapsed: 1.09s