线程停放(Thread Parking)
- 优点:
- 低资源消耗:当线程停放时,它不占用CPU时间片,处于几乎不消耗系统资源的状态。例如在一个需要大量线程等待特定条件触发的应用中,停放线程可显著降低系统资源占用。
- 灵活唤醒:可以通过特定的机制(如
std::thread::park
和std::thread::unpark
)精确控制线程的唤醒时机,对于需要精细控制线程执行顺序的场景很有用。
- 缺点:
- 复杂性:使用线程停放需要手动管理线程的状态,包括何时停放和唤醒,实现不当容易导致死锁或逻辑错误。
- 缺乏通用同步语义:线程停放本身不具备像锁那样的互斥访问语义,不能直接用于保护共享资源。
Mutex(互斥锁)
- 优点:
- 保护共享资源:提供了简单且可靠的机制来保护共享资源,确保同一时间只有一个线程可以访问共享数据,防止数据竞争。例如在多线程访问全局变量时,使用
Mutex
可保证数据的一致性。
- 通用性:适用于大多数需要保护共享资源的并发场景,使用方式较为直观。
- 缺点:
- 性能开销:获取和释放锁会带来一定的性能开销,尤其是在高并发场景下频繁获取和释放锁时,可能成为性能瓶颈。
- 死锁风险:如果多个线程以不一致的顺序获取多个锁,可能会导致死锁。
Condvar(条件变量)
- 优点:
- 线程间通信:与
Mutex
结合使用,可以实现线程间的高效通信。一个线程可以等待某个条件满足,其他线程在条件满足时唤醒等待的线程。例如在生产者 - 消费者模型中,消费者线程可以等待生产者线程生产数据后再消费。
- 避免不必要的唤醒:通过条件判断,只有当条件真正满足时才唤醒线程,避免了像
notify_all
那样可能导致的不必要唤醒,提高了效率。
- 缺点:
- 依赖Mutex:
Condvar
本身不能单独使用,必须与Mutex
配合,增加了代码的复杂性。
- 虚假唤醒:某些系统可能会出现虚假唤醒的情况,即线程在条件未满足时被唤醒,需要在代码中进行额外的条件检查。
适合使用线程停放的场景
- 资源敏感场景:例如在嵌入式系统或资源有限的环境中,需要大量线程等待特定事件发生,但又要尽量减少资源消耗。可以将这些线程停放,当事件发生时再唤醒它们。
use std::thread;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
fn main() {
let flag = Arc::new(AtomicBool::new(false));
let flag_clone = flag.clone();
let handle = thread::spawn(move || {
while!flag_clone.load(Ordering::Relaxed) {
thread::park();
}
println!("Thread woke up and flag is set!");
});
thread::sleep(std::time::Duration::from_secs(2));
flag.store(true, Ordering::Relaxed);
handle.unpark();
handle.join().unwrap();
}
- 精细控制执行顺序场景:当需要精确控制线程的执行顺序,且不需要像锁那样保护共享资源时。比如在一个复杂的多线程任务调度系统中,某些线程需要等待其他线程完成特定步骤后再执行,就可以使用线程停放来实现这种精细控制。