如何使用Condvar限制多线程?

时间:2015-04-25 21:16:07

标签: multithreading rust

我正在尝试使用Condvar来限制在任何给定时间处于活动状态的线程数。我很难找到关于如何使用Condvar的好例子。到目前为止,我有:

use std::sync::{Arc, Condvar, Mutex};
use std::thread;

fn main() {
    let thread_count_arc = Arc::new((Mutex::new(0), Condvar::new()));
    let mut i = 0;
    while i < 100 {
        let thread_count = thread_count_arc.clone();
        thread::spawn(move || {
            let &(ref num, ref cvar) = &*thread_count;
            {
                let mut start = num.lock().unwrap();
                if *start >= 20 {
                    cvar.wait(start);
                }
                *start += 1;
            }
            println!("hello");
            cvar.notify_one();
        });
        i += 1;
    }
}

给出的编译器错误是:

error[E0382]: use of moved value: `start`
  --> src/main.rs:16:18
   |
14 |                     cvar.wait(start);
   |                               ----- value moved here
15 |                 }
16 |                 *start += 1;
   |                  ^^^^^ value used here after move
   |
   = note: move occurs because `start` has type `std::sync::MutexGuard<'_, i32>`, which does not implement the `Copy` trait

我完全不确定我对Condvar的使用是否正确。我尝试尽可能接近Rust API上的示例。 Wwat是实现这个的正确方法吗?

4 个答案:

答案 0 :(得分:4)

这是一个编译版本:

use std::sync::{Arc, Condvar, Mutex};
use std::thread;

fn main() {
    let thread_count_arc = Arc::new((Mutex::new(0u8), Condvar::new()));
    let mut i = 0;
    while i < 100 {
        let thread_count = thread_count_arc.clone();
        thread::spawn(move || {
            let &(ref num, ref cvar) = &*thread_count;
            {
                let mut start = num.lock().unwrap();
                while *start >= 20 {
                    start = cvar.wait(start).unwrap()
                }
                *start += 1;
            }
            println!("hello");
            cvar.notify_one();
        });
        i += 1;
    }
}

wait API

可以看到重要的部分
pub fn wait<'a, T>(
    &self, 
    guard: MutexGuard<'a, T>
) -> LockResult<MutexGuard<'a, T>>

这表示wait 消费 guard,这就是您收到错误的原因 - 您不再拥有start,因此您可以&# 39;打电话给它任何方法!

这个功能实际上很好地反映了Condvar的工作方式 - 你放弃了Mutex(由start表示)一段时间的锁定,以及函数返回你再次获得锁定。

修复是放弃锁定,然后从wait获取锁定保护返回值。我也从if切换到whileencouraged by huon

答案 1 :(得分:1)

作为参考,在给定范围内拥有有限数量的线程的常用方法是使用Semaphore

不幸的是,Semaphore从未稳定,在Rust 1.8中被弃用,并在Rust 1.9中被删除。有些crates可以在其他并发原语之上添加信号量。

let sema = Arc::new(Semaphore::new(20)); 

for i in 0..100 {
    let sema = sema.clone();
    thread::spawn(move || {
        let _guard = sema.acquire();
        println!("{}", i);
    })
}

这不是完全相同的事情:因为当该线程进入时,每个线程都不会打印范围内的线程总数。

答案 2 :(得分:0)

我意识到我提供的代码并没有完全符合我的要求,因此我将此编辑of Shepmaster's code放在此处以供将来参考。

use std::sync::{Arc, Condvar, Mutex};
use std::thread;

fn main() {
    let thread_count_arc = Arc::new((Mutex::new(0u8), Condvar::new()));
    let mut i = 0;
    while i < 150 {
        let thread_count = thread_count_arc.clone();
        thread::spawn(move || {
            let x;
            let &(ref num, ref cvar) = &*thread_count;
            {
                let start = num.lock().unwrap();
                let mut start = if *start >= 20 {
                    cvar.wait(start).unwrap()
                } else {
                    start
                };
                *start += 1;
                x = *start;
            }
            println!("{}", x);
            {
                let mut counter = num.lock().unwrap();
                *counter -= 1;
            }
            cvar.notify_one();
        });
        i += 1;
    }
    println!("done");
}

在操场上运行它应该会显示或多或少的预期行为。

答案 3 :(得分:-1)

您要使用while循环,并在每次迭代时重新分配start,例如:

fn main() {
    let thread_count_arc = Arc::new((Mutex::new(0), Condvar::new()));
    let mut i = 0;
    while i < 100 {
        let thread_count = thread_count_arc.clone();
        thread::spawn(move || {
            let &(ref num, ref cvar) = &*thread_count;
            let mut start = num.lock().unwrap();
            while *start >= 20 {
                let current = cvar.wait(start).unwrap();
                start = current;
            }
            *start += 1;
            println!("hello");
            cvar.notify_one();
        });
        i += 1;
    }
}

另请参阅有关该主题的文章:

https://medium.com/@polyglot_factotum/rust-concurrency-five-easy-pieces-871f1c62906a

https://medium.com/@polyglot_factotum/rust-concurrency-patterns-condvars-and-locks-e278f18db74f

相关问题