Course contentsShow
← Rust
Lesson 1170 of 1,91010. Concurrency PrimitivesFree lesson

Multiplexing Tasks with Selects

You will await multiple concurrent futures and process the first one to resolve.

In this lesson, you will learn how to coordinate multiple concurrent operations by waiting for the first one to reach completion using the select! macro.

The Problem of Sequential Waiting

When managing multiple asynchronous tasks, you often find yourself in a situation where you need to react to whichever event happens first. If you simply await task A and then task B, your program will block on the completion of task A, even if task B finishes much earlier. This leads to inefficiency, especially in systems like network servers or UI event loops where responsiveness is critical.

The select! Solution

The select! macro provides a way to poll multiple futures simultaneously. Think of it as a "race" between different branches of code. The runtime concurrently polls all branches provided to the select! block, and as soon as one branch completes, the others are dropped, and the successful branch's result is returned.

Consider this example using tokio::select!:

use tokio::sync::oneshot;

async fn multiplex_tasks(rx1: oneshot::Receiver<i32>, rx2: oneshot::Receiver<i32>) {
    tokio::select! {
        val = rx1 => println!("Received from task 1: {:?}", val),
        val = rx2 => println!("Received from task 2: {:?}", val),
    }
}

How it Works

When select! is invoked:

  1. It polls each future provided in the block.
  2. If any future is ready, it executes that branch and terminates the others.
  3. If no future is ready, it yields control back to the executor, ensuring that the task is woken up when any of the internal futures make progress.

This pattern is essential for implementing timeouts, handling cancellation signals, or managing multiple concurrent streams of data without forcing a rigid order of execution. Because select! effectively "cancels" the branches that didn't win the race, it is crucial to ensure that your futures handle dropped state cleanly to avoid resource leaks.

Key Takeaway: The select! macro allows you to multiplex multiple futures into a single execution flow, letting your task process the first available result while safely discarding the remaining pending operations.