How to check if a thread has finished in Rust?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Rust's std::thread module spawns OS threads that run independently. Unlike some languages, Rust does not provide a built-in is_finished() method on JoinHandle (until Rust 1.61+). The primary mechanism is join(), which blocks until the thread completes. For non-blocking checks, you can use JoinHandle::is_finished() (stabilized in Rust 1.61), channels, atomic flags, or Arc<Mutex<bool>>.
Method 1: join() — Blocking Wait
join() blocks the calling thread until the spawned thread finishes:
join() returns Result<T, Box<dyn Any>>. If the thread panicked, join() returns Err containing the panic payload:
Method 2: is_finished() — Non-Blocking Check (Rust 1.61+)
JoinHandle::is_finished() returns true if the thread has completed:
This is the simplest non-blocking approach, but polling in a loop is wasteful. Prefer channels or condition variables for event-driven notification.
Method 3: Channels for Completion Notification
Use mpsc::channel to receive a signal when the thread finishes:
Method 4: Shared Atomic Flag
Use AtomicBool for a lightweight completion flag:
Method 5: Condvar for Efficient Waiting
Use a condition variable to wait without busy-polling:
Managing Multiple Threads
Comparison of Methods
| Method | Blocking | Notification | Complexity |
join() | Yes | On completion | Simplest |
is_finished() | No (poll) | Polling | Simple, Rust 1.61+ |
mpsc::channel | Optional | Event-driven | Moderate |
AtomicBool | No (poll) | Polling | Low overhead |
Condvar | Efficient wait | Event-driven | Most flexible |
Common Pitfalls
- Forgetting to
join(): If you drop aJoinHandlewithout callingjoin(), the thread is detached and continues running. The program may exit before the thread finishes, losing its work. - Panics in threads: A panic in a spawned thread does not crash the main thread. It is silently contained until
join()is called, which returnsErr. Always handle theResultfromjoin(). - Busy-polling waste: Using
is_finished()orAtomicBoolin a tight loop withoutthread::sleep()burns CPU. Use condition variables or channels for efficient waiting. - Ordering on atomics: Use
Ordering::Releasewhen writing andOrdering::Acquirewhen reading the flag.Relaxedordering may cause the reader to see stale values on some architectures. - Deadlocks with Mutex: If the thread holds a
Mutexlock when it panics, the Mutex becomes poisoned. Subsequentlock()calls returnErr. Uselock().unwrap_or_else(|e| e.into_inner())to recover from poisoned mutexes if appropriate.
Summary
- Use
handle.join()to block until a thread completes — simplest and most common - Use
handle.is_finished()(Rust 1.61+) for non-blocking polling - Use
mpsc::channelwithtry_recv()for event-driven completion notification - Use
Condvarfor efficient waiting without busy-polling - Always call
join()to collect the result and handle potential panics — dropping aJoinHandledetaches the thread
Related reading
- How to check if current thread is not main thread
- How to check if current thread is not main thread
- How to check if current thread is not main thread
- How to check possibility of deadlock in c code
- How to combine python asyncio with threads?
- How to configure a fine tuned thread pool for futures?
- How to consume WinRT IAsyncOperation object in native c environment
- How to convert a function in a third party library to be async?
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.