Classic never-ending thread loop using tasks?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
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

