Concepts / Spawning Tasks on Separate Threads

Spawning Tasks on Separate Threads

A future is a mechanism for returning a value from a task spawned on a separate thread.

  • Programming

Why the Caller Cannot Wait Empty-Handed

When a task is spawned on a separate thread, the code that started it keeps running. The task may finish some time later, but the caller still needs its result. This creates a practical problem: how does a value produced on one thread get back to the code that needs it on another thread?

The source introduces futures and promises as the mechanism for this job. As you read, keep one question in mind throughout: at any moment, is the value already available, or is it still on its way? Most of the reasoning about futures and promises comes down to answering that question correctly.

The Claim Ticket Analogy

Think of a task on a separate thread as a piece of work you hand to someone else. You do not get the answer back immediately. Instead, you receive a claim ticket that will be honored when the answer exists. The ticket is not the answer. It is the thing you can check and collect from later.

What a Future Does

Definition

A future is a mechanism for returning a value from a task spawned on a separate thread.

Because the value is produced on a separate thread, the future is the natural place to wait for that value and collect it once it is ready. Its purpose is narrow and specific: moving a result from a task back to the code that needs it. The future is the handle the caller holds. It is the thing the caller reads from.

What a Promise Does

A promise works together with a future for handling the task's return value. The useful way to keep the two ideas apart is this: the future is what you read from, and the promise is what the task uses to make the value available. Neither one is useful on its own in this pattern. The value travels from one to the other.

The relationship is a linked pair. The task running on a separate thread is connected to a promise, the promise is linked to a future, and the future returns the value to the caller. Reading the chain from left to right in terms of data flow: the task is where the value comes from, the promise is the channel that carries it, the future is the handle the caller holds, and the calling code is where the value is finally used.

Tracing the Pair from Waiting to Read

The pair moves through a small number of states. It begins in a waiting state, in which the future has no value yet. The only change that moves it forward is the value being supplied through the promise. Once that happens, the future has something to return. Reading it is the final step. The diagram below traces these states in order.

Waitingno value yetValue suppliedthrough the promiseValue readyfuture can return itValue readby the callertask deliversfuture receivescaller reads
What state is the future in before the spawned task sets a value, when the value arrives, and after the caller reads it?

Notice that the waiting state is the first state, and it is the one learners most often underestimate. Nothing has gone wrong while the future is waiting. Only the supply of the value through the promise moves the pair forward, and only reading the future completes the trace.

Following the Value Across Threads

The value has a direction. It starts in the spawned task, moves through the promise, is held by the future, and is finally used by the calling code. The next diagram shows only that movement, without the state labels.

Spawned taskproduces the valuePromisecarries the valueFuturehandle the caller holdsCalling codeuses the valuesupplies valuelinked toreturns value
How does the value move from the spawned thread's promise to the future the caller holds?

A useful check is to ask, at each arrow, whether the value has already crossed it. Before the task supplies the value, nothing has crossed the first arrow, so the future has nothing to return.

Two Threads Running Side by Side

Once the task is spawned, the caller and the spawned task proceed on their own threads. The code that started the task keeps running, so the two threads move forward independently until the caller needs the result. The timeline below shows one possible ordering of these events. The order of the spawned task's finish relative to the caller's progress can vary, which is exactly why the caller must check whether the value is available before using it.

Spawn taskcaller gets a futureCaller continuesother work runsTask runson separate threadValue suppliedthrough promiseCaller reads futurevalue collectedkeeps runningtask beginsfinishesneeds resultvalue available
In what order do the main thread and the spawned thread proceed, and where does the caller hold its future and read it?

Worked Trace of One Handoff

Worked example

Tracing a single handoff from spawn to read

A caller spawns a task on a separate thread. The caller later needs the task's return value. Trace the state of the future-and-promise pair at each point: right after spawning, after the value is supplied, and after the caller reads it.

  1. 1
    Right after spawning

    The caller receives a future immediately, as a placeholder. The pair is in its waiting state because no value has been supplied through the promise yet. This is expected, not an error.

  2. 2
    While the task runs

    The caller keeps running. The task on the separate thread is still producing its result. Nothing has yet moved through the promise, so the future still has nothing to return.

  3. 3
    After the value is supplied

    The task delivers its value through the promise. The future now has something to return. The pair has left the waiting state and is ready for the caller to read.

  4. 4
    After the caller reads it

    The caller reads the future and collects the value. Reading the future is the final step in the trace.

Result

The pair moves from waiting, to a value being ready, to the value being read. The only event that moves it forward from waiting is the value being supplied through the promise.

What do you think happens?

Right after the task is spawned, and before it has supplied any value, is the future in an error state?

  • Yes, the caller should treat it as a failure

  • No, a waiting future is a valid and expected state

Reveal answer
Answer:

No, a waiting future is a valid and expected state

The source is explicit that a future may be waiting for its value, and that this waiting state is expected rather than an error. Only the arrival of the value through the promise moves the pair forward.

Mistakes Learners Make with Futures

  • Common mistake

    Treating the future as the answer itself

    What it looks like

    Reading a future right after spawning the task and assuming the result is already in hand.

    Why it fails

    The source says the ticket is not the answer. The future is the thing you check and collect from later, and the value may not have arrived yet.

    Do this instead

    Recognize that the future is a handle. Check whether the value is available before relying on it.

  • Common mistake

    Reading a waiting future as an error

    What it looks like

    Concluding that something broke because the future has no value yet.

    Why it fails

    A future that has not yet received its value is an expected situation. Waiting is part of the normal flow of the pattern.

    Do this instead

    Treat the waiting state as the first valid state in the trace, not as a failure.

  • Common mistake

    Thinking the future alone moves the value

    What it looks like

    Assuming the caller's read is what causes the value to be produced.

    Why it fails

    The value comes from the task and travels through the promise. The future is only where the caller holds and collects it.

    Do this instead

    Follow the value from the task, through the promise, to the future, and finally to the caller.

  • Common mistake

    Forgetting that the caller keeps running after the task is spawned

Practice Tracing a Pair

EASY

A caller spawns a task on a separate thread and immediately receives a future. Describe the state of the future-and-promise pair at three moments: immediately after spawning, after the spawned task supplies its value through the promise, and after the caller reads the future. For each moment, say which event, if any, moved the pair into that state.

Hints
  • Start from the waiting state, which is where the pair begins before any value arrives.
  • Ask which single event moves the pair forward from waiting.
  • The final state happens only after the caller reads the future.

Key Takeaways

  • A future is a mechanism for returning a value from a task spawned on a separate thread. It is the handle the caller holds and reads from.
  • A promise works together with a future. The task uses the promise to make the value available, and the value travels from the promise to the future.
  • The pair begins in a waiting state. That state is expected and is not an error. Only the value being supplied through the promise moves the pair forward.
  • After the task is spawned, the caller keeps running. The central question is always whether the value is already available or still on its way.
  • The trace ends when the caller reads the future and collects the value.

Related concepts

More concepts starting with S