[C#] Correct Usage of Task.Delay for Non-Blocking Waits

When pausing execution for a certain period in C#, using the traditional Thread.Sleep completely stops (blocks) the running thread. This causes UI freezes or thread pool exhaustion in web servers.

In modern .NET development, the standard approach is to use Task.Delay combined with async/await to perform a “wait” while keeping the thread free.

Below, I will explain an implementation example of efficient waiting, assuming a network connection retry scenario.

目次

Table of Contents

  1. Implementing Asynchronous Waiting with Task.Delay
  2. Sample Code
  3. Explanation and Technical Points

1. Implementing Asynchronous Waiting with Task.Delay

In this sample, we assume a server connection failure and set a certain interval (wait time) before the next retry. By using await Task.Delay, resources like the main thread are released for other processing during the wait.

2. Sample Code

using System;
using System.Threading.Tasks;

public class Program
{
    public static async Task Main()
    {
        Console.WriteLine("Starting system integration process...");

        const int maxRetries = 3;
        
        for (int attempt = 1; attempt <= maxRetries; attempt++)
        {
            Console.WriteLine($"\n[{DateTime.Now:HH:mm:ss}] Attempting connection to external API... (Attempt: {attempt}/{maxRetries})");

            // Simulate 500ms processing time instead of actual communication
            await Task.Delay(500);

            if (attempt < maxRetries)
            {
                // Scenario: Wait until next attempt upon failure
                // Unlike Thread.Sleep(2000), the thread is not locked during this wait
                int waitSeconds = 2;
                Console.WriteLine($"-> Connection failed. Waiting {waitSeconds} seconds before retrying.");

                // Wait asynchronously for the specified milliseconds
                await Task.Delay(waitSeconds * 1000);
            }
            else
            {
                Console.WriteLine("-> Connection failed. Ending all retries.");
            }
        }

        Console.WriteLine($"\n[{DateTime.Now:HH:mm:ss}] Process ended");
    }
}

3. Explanation and Technical Points

Difference from Thread.Sleep

  • Thread.Sleep(1000): The CPU forcibly stops the thread’s processing for 1 second. If done on the UI thread, the screen freezes completely for 1 second and accepts no operations.
  • await Task.Delay(1000): The compiler generates a state machine and logically reserves “execution to continue after 1 second.” During the wait, the thread is released and used for UI rendering or processing other requests.

How to Specify Arguments

Task.Delay has overloads that accept not only milliseconds (int) but also TimeSpan. Using this is recommended for better readability.

// Waiting for 1.5 seconds is more intuitive than calculating milliseconds
await Task.Delay(TimeSpan.FromSeconds(1.5));

Handling Cancellation

Task.Delay can accept a CancellationToken. This allows control such as “Planning to wait 5 seconds, but stopping immediately if the user presses a cancel button.”

// Cancellable wait
await Task.Delay(5000, cancellationToken);
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次