Function std::thread::park 1.0.0[−][src]
pub fn park()
Expand description
阻塞,除非或直到当前线程的 token 可用为止。
对 park
的调用不能保证线程将永远保持驻留状态,因此调用者应为此做好准备。
park 和 unpark
每个线程都通过 thread::park
函数和 thread::Thread::unpark
方法提供了一些基本的阻塞支持。
park
阻塞当前线程,然后可以通过在阻塞线程的句柄上调用 unpark
方法从另一个线程恢复该线程。
从概念上讲,每个 Thread
句柄都有一个关联的 token,该 token 最初不存在:
-
thread::park
函数会阻塞当前线程,除非或直到 token 可用于其线程句柄为止,否则该原子将自动消耗 token。 它也可能虚假地返回,而不消耗 token。thread::park_timeout
这样做是一样的,但是允许指定最长的时间来阻止线程。 -
Thread
上的unpark
方法原子地使 token (如果尚未提供) 可用。 由于最初不存在 token,因此unpark
后跟park
将导致第二个调用立即返回。
换句话说,每个 Thread
的行为都类似于自旋锁,可以使用 park
和 unpark
进行锁定和解锁。
请注意,被取消阻止并不意味着与取消该线程的某个人进行任何同步,这也可能是虚假的。
例如,将 park
和 unpark
都立即返回而无需执行任何操作将是有效但效率低下的实现。
通常通过获取当前线程的句柄,将该句柄放置在共享数据结构体中,以便其他线程可以找到它,然后 park
在循环中来使用该 API。
当满足某些所需条件时,另一个线程将在句柄上调用 unpark
。
这种设计的动机是双重的:
-
在构建新的同步原语时,它无需分配互斥锁和 condvar。线程已经提供了基本的 blocking/signaling。
-
它可以在许多平台上非常有效地实现。
Examples
use std::thread; use std::sync::{Arc, atomic::{Ordering, AtomicBool}}; use std::time::Duration; let flag = Arc::new(AtomicBool::new(false)); let flag2 = Arc::clone(&flag); let parked_thread = thread::spawn(move || { // 我们要等到标志被设置。 // 我们可以旋转,但是使用 park/unpark 效率更高。 while !flag2.load(Ordering::Acquire) { println!("Parking thread"); thread::park(); // 我们可以伪装地到达这里,即在下面的 10ms 结束之前! // 但这没问题,我们一直处于循环状态,直到仍然设置了标志。 println!("Thread unparked"); } println!("Flag received"); }); // 花费一些时间来生成线程。 thread::sleep(Duration::from_millis(10)); // 设置标志,并让线程唤醒。 // 这里没有竞态条件,如果 `unpark` 首先出现,则 `park` 将立即返回。 // 因此,没有死锁的风险。 flag.store(true, Ordering::Release); println!("Unpark the thread"); parked_thread.thread().unpark(); parked_thread.join().unwrap();Run