Return value from thread
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Plain threads usually do not "return" values the way normal functions do. The usual pattern is to write the result somewhere the caller can read later, or to use a higher-level abstraction such as a future that wraps the thread result for you.
Why join() Is Not a Return Value
Many beginners assume thread.join() gives back the worker result. In most threading APIs, join() only waits for completion. It does not automatically carry the function's output to the caller.
That means you need one of these designs:
- shared mutable state
- a queue
- a promise or future
- a thread pool API that already exposes
.result()
The best choice depends on whether you want one result, many results, streaming results, or exception handling.
The Simplest Pattern: Shared Container
If you are writing a small example, a worker can store its result into a shared object.
This works, but it scales badly. Once you have multiple workers, failures, or partial results, ad hoc shared containers become messy.
A Better Basic Pattern: Queue
A queue is a safer handoff mechanism because the worker produces a value and the caller consumes it explicitly.
This pattern is nice because:
- the result handoff is explicit
- multiple workers can all push into the same queue
- you can also push error markers or structured messages
The Most Convenient Pattern: ThreadPoolExecutor
If your language or library offers futures, prefer them. In Python, concurrent.futures.ThreadPoolExecutor is usually the cleanest way to get a result from threaded work.
This solves several problems at once:
- you get the return value directly
- exceptions are preserved and re-raised on
result() - you do not need to build your own synchronization protocol
For many modern programs, this is the answer to "How do I return a value from a thread?"
Handling Exceptions Matters Too
A result channel should also handle failure. Futures do this well.
If you used a plain shared variable instead, you would need your own error-signaling convention.
Multiple Results
If several threads produce results, futures still work well.
This is often better than manually joining threads and then reading from shared lists, because the completion order and error handling are already structured.
What About Other Languages
The idea is the same across languages:
- Java uses
CallableandFuture - C# uses
Task<T> - C++ uses
std::future - JavaScript workers usually post messages back rather than returning directly
So the general rule is stable even when syntax changes: raw thread objects are low-level execution tools, while result-bearing abstractions live one layer above them.
When a Shared Variable Is Still Fine
There are cases where a shared variable is enough:
- a tiny script
- one worker
- simple immutable result
- obvious lifecycle
But once the code grows, queues and futures are easier to reason about and easier to extend.
Common Pitfalls
- Expecting
join()to give the worker's return value. - Using shared mutable state without synchronization or a clear ownership rule.
- Ignoring exceptions raised in the worker and only checking for "missing result."
- Overusing raw threads when a future-based API already exists.
- Forgetting that multiple workers writing into the same container need coordination.
Summary
- Threads usually do not return values directly through
join(). - Small programs can use a shared container, but queues are cleaner.
- Future-based APIs such as
ThreadPoolExecutorare usually the most practical solution. - Futures also handle exceptions and multiple tasks better than ad hoc shared variables.
- If you want a result-bearing abstraction, prefer a higher-level concurrency API over raw threads.
Related reading
- return value of a callback function in Node.js
- Returning a value from thread?
- Returning a value from thread?
- Returning value from Thread
- RMI alternatives for bidirectional asynchronous calls and callbacks through firewalls or NAT
- Row was updated or deleted by another transaction or unsaved-value mapping was incorrect
- Ruby concurrency non-blocking I/O vs threads
- Run a process asynchronously and read from stdout and stderr
.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.