Asynchronous Programming
Task Management
Coding Techniques
Software Development
Concurrency

How to Pause and Resume the Task which runs Asynchronously

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

An asynchronous task usually cannot be "paused" externally in the same way a debugger pauses a thread. In practice, pause and resume must be designed cooperatively into the task itself. The task checks a synchronization primitive at safe points, waits when paused, and continues when resumed.

Why Task Does Not Have Built-In Pause/Resume

In C#, Task represents an asynchronous operation, not a schedulable thread that the runtime can safely freeze and restart at arbitrary instructions. If .NET exposed a force-pause API, it would risk deadlocks, broken invariants, and paused code while holding locks.

That is why a pauseable async workflow is normally written as a loop that cooperates with a gate object.

A Practical Pattern With ManualResetEventSlim

One simple pattern is to use ManualResetEventSlim as a pause gate. When the gate is set, work continues. When the gate is reset, the task waits.

csharp
1using System;
2using System.Threading;
3using System.Threading.Tasks;
4
5class Worker
6{
7    private readonly ManualResetEventSlim _pauseGate = new ManualResetEventSlim(true);
8    private readonly CancellationTokenSource _cts = new CancellationTokenSource();
9
10    public async Task RunAsync()
11    {
12        int step = 0;
13
14        while (!_cts.Token.IsCancellationRequested)
15        {
16            _pauseGate.Wait(_cts.Token);
17
18            Console.WriteLine($"Working on step {step}");
19            step++;
20
21            await Task.Delay(500, _cts.Token);
22        }
23    }
24
25    public void Pause() => _pauseGate.Reset();
26    public void Resume() => _pauseGate.Set();
27    public void Stop() => _cts.Cancel();
28}
29
30class Program
31{
32    static async Task Main()
33    {
34        var worker = new Worker();
35        var task = worker.RunAsync();
36
37        await Task.Delay(1200);
38        worker.Pause();
39        Console.WriteLine("Paused");
40
41        await Task.Delay(1500);
42        worker.Resume();
43        Console.WriteLine("Resumed");
44
45        await Task.Delay(1200);
46        worker.Stop();
47
48        try
49        {
50            await task;
51        }
52        catch (OperationCanceledException)
53        {
54            Console.WriteLine("Stopped");
55        }
56    }
57}

This works because the task checks the pause gate at a safe boundary inside the loop.

Making The Pause Fully Async-Friendly

ManualResetEventSlim.Wait blocks the current thread. For CPU-bound background work that may be fine, but for highly asynchronous code you may prefer a truly async gate built with SemaphoreSlim or a task-based signal.

A small async-friendly pattern is to wait on a TaskCompletionSource when paused and replace it when resumed. That avoids blocking a thread pool thread.

csharp
1using System;
2using System.Threading.Tasks;
3
4class AsyncPauseToken
5{
6    private volatile TaskCompletionSource<bool>? _paused;
7
8    public Task WaitWhilePausedAsync() => _paused?.Task ?? Task.CompletedTask;
9
10    public void Pause()
11    {
12        _paused ??= new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
13    }
14
15    public void Resume()
16    {
17        _paused?.TrySetResult(true);
18        _paused = null;
19    }
20}

You would then call await pauseToken.WaitWhilePausedAsync() inside the worker loop.

Design The Task Around Safe Pause Points

The important design rule is that pausing should happen at boundaries where the operation can safely stop and later continue. Examples include:

  • between work items in a queue
  • between batches of file processing
  • between iterations of a polling loop

Pausing in the middle of a transaction, while holding a lock, or after partially mutating shared state can create hard-to-debug failures. Cooperative pause only works well when the task structure already has natural checkpoints.

Cancellation Is A Different Concern

Pause and cancellation are not the same feature. Pausing means "wait and continue later." Cancellation means "stop and unwind now."

A robust design often supports both. The example above uses ManualResetEventSlim for pause/resume and CancellationTokenSource for shutdown.

Common Pitfalls

A common mistake is looking for a built-in Task.Pause() API. .NET does not provide one for general tasks.

Another mistake is pausing code while it holds locks or owns partially updated shared state. Resume may work, but the pause itself can block unrelated parts of the system.

Developers also sometimes block inside code that should stay fully asynchronous. If thread blocking matters, use an async-friendly pause token instead of Wait().

Finally, do not overload cancellation to mean pause. They are different lifecycle events and should be modeled separately.

Summary

  • Pause and resume in async code must be cooperative.
  • 'Task does not support arbitrary external suspension.'
  • A ManualResetEventSlim gate is a practical solution for many worker loops.
  • For non-blocking async flows, use an awaitable pause token pattern.
  • Add pause checks only at safe boundaries in the workflow.
  • Treat pause/resume and cancellation as separate behaviors.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track 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.

Browse interview questions

All Rights Reserved.