Classic never-ending thread loop using tasks?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
A classic never-ending worker loop can be implemented with Task, but the important part is not the while (true) itself. The important part is cancellation, error handling, and avoiding a busy spin. In modern C# code, a long-running loop should usually watch a CancellationToken and either await work or sleep briefly rather than consuming CPU continuously.
Basic Pattern with Task.Run
A minimal background loop looks like this:
This is the modern equivalent of the old infinite worker thread pattern, but with cooperative shutdown.
Why while (true) Alone Is Not Enough
A raw infinite loop is dangerous if it does not wait for anything.
That loop will peg a CPU core. A proper worker loop should either:
- wait for incoming work
- delay between polling attempts
- block on a queue or synchronization primitive
So the real goal is not "never-ending." It is "long-lived and controllable."
Use Cancellation Tokens
The cancellation token is what turns a hard-to-stop infinite loop into a cooperative background worker.
Benefits:
- clean shutdown
- easier integration with application lifetime
- better testability
- fewer abandoned background tasks
Without cancellation, the task may outlive the part of the program that created it.
Prefer Waiting for Real Work
Polling with Task.Delay is acceptable for some jobs, but if work arrives through a queue, waiting on the queue is usually better.
This is often better than a timed loop because the task sleeps until work actually exists.
When BackgroundService Is Better
If you are in ASP.NET Core or another hosted environment, a naked Task.Run is often not the best place to manage long-lived background work. A hosted service or BackgroundService gives you lifecycle integration.
That is important because application shutdown, logging, and dependency injection are already wired into the hosting model.
So a good rule is:
- standalone app:
Task.Runcan be fine - hosted app: prefer a proper background service abstraction
Handle Exceptions Explicitly
A long-running task that throws unexpectedly can die silently if nobody observes it carefully. Wrap the loop body with clear error handling or ensure the task is awaited and monitored.
That matters even more for endless loops, because the worker may appear healthy until it crashes once and stops forever.
Common Pitfalls
The biggest mistake is using a tight while (true) loop with no waiting, which wastes CPU.
Another mistake is launching a background task with no cancellation path.
A third issue is ignoring exceptions from the worker, which can make the loop die without the rest of the program noticing.
Summary
- A classic never-ending worker loop can be modeled with
Task, but it should be cancellable - Use
CancellationTokeninstead of relying on a raw infinite loop - Avoid busy spinning; wait for work or delay between polling iterations
- Use queues or hosted background services when the application architecture supports them
- Treat lifecycle and exception handling as part of the design, not as optional cleanup
Related reading
- Clean pattern to manage multi-step async processes on a tree
- Cleanest Way to Invoke Cross-Thread Events
- Clojure - Core.async Pipeline take confusion
- Clone a String for an async move closure in Rust
- Closing a blocking queue
- Collect global top-k from each node's list in MPI
- Combining Unknown number of Observables in RxJava
- Commit Asynchronously a message just after reading from topic
.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.