404 Not Found

404 Not Found


nginx

Asynchronous Programming

Synchronous vs Asynchronous

Synchronous code executes line by line in order. When it encounters a time-consuming operation, it blocks the current thread, making the program unresponsive. Asynchronous code releases the thread while waiting for time-consuming operations, allowing the program to continue doing other work.

Problems with synchronous blocking:

Advantages of asynchronous:

Example

CSHARP
using System;
using System.Threading;

Console.WriteLine("Sync start: " + DateTime.Now.ToString("HH:mm:ss"));
Thread.Sleep(2000);
Console.WriteLine("Sync end: " + DateTime.Now.ToString("HH:mm:ss"));
▶ Try it Yourself
TEXT
Sync start: 10:00:00
Sync end: 10:00:02

Task and Task<T>

Task represents an asynchronous operation that does not return a result. Task<T> represents an asynchronous operation that returns a result of type T. They are the core types of C# asynchronous programming.

Type Description Example
Task Async operation with no return value Task.Delay(1000)
Task<T> Async operation with a return value Task.FromResult(42)

Example

CSHARP
using System;
using System.Threading.Tasks;

Task noResultTask = Task.Delay(500);
Task<int> resultTask = Task.FromResult(42);

await noResultTask;
int value = await resultTask;
Console.WriteLine("Retrieved value: " + value);
▶ Try it Yourself
TEXT
Retrieved value: 42

async/await Syntax

The async modifier marks a method as asynchronous, and the await operator waits for an asynchronous operation to complete without blocking the thread. await can only be used inside an async method.

Syntax rules:

Example

CSHARP
using System;
using System.Threading.Tasks;

async Task DoWorkAsync()
{
    Console.WriteLine("Work start: " + DateTime.Now.ToString("HH:mm:ss.fff"));
    await Task.Delay(1000);
    Console.WriteLine("Work end: " + DateTime.Now.ToString("HH:mm:ss.fff"));
}

async Task<string> GetMessageAsync()
{
    await Task.Delay(500);
    return "Async message arrived";
}

await DoWorkAsync();
string msg = await GetMessageAsync();
Console.WriteLine(msg);
▶ Try it Yourself
TEXT
Work start: 10:00:00.000
Work end: 10:00:01.000
Async message arrived

Task.Run

Task.Run offloads CPU-intensive work to the thread pool for execution, avoiding blocking the current thread. Suitable for compute-intensive operations, not for I/O operations.

Example

CSHARP
using System;
using System.Threading.Tasks;

int ComputeHeavy()
{
    int result = 0;
    for (int i = 0; i < 100_000_000; i++)
    {
        result += i;
    }
    return result;
}

Console.WriteLine("Starting computation...");
int total = await Task.Run(() => ComputeHeavy());
Console.WriteLine("Computation result: " + total);
▶ Try it Yourself
TEXT
Starting computation...
Computation result: 4999999950000000

Task.WhenAll / WhenAny / Delay

Method Description
Task.WhenAll Waits for all Tasks to complete
Task.WhenAny Waits for any one Task to complete
Task.Delay Asynchronously waits for the specified number of milliseconds, replacing Thread.Sleep

Example

CSHARP
using System;
using System.Threading.Tasks;

async Task<string> FetchAsync(string site)
{
    await Task.Delay(new Random().Next(500, 1500));
    return site + " loaded";
}

Task<string> t1 = FetchAsync("SiteA");
Task<string> t2 = FetchAsync("SiteB");
Task<string> t3 = FetchAsync("SiteC");

string[] all = await Task.WhenAll(t1, t2, t3);
Console.WriteLine("--- WhenAll complete ---");
foreach (string s in all)
{
    Console.WriteLine(s);
}

Task<string> first = await Task.WhenAny(t1, t2, t3);
Console.WriteLine("First completed: " + first.Result);
▶ Try it Yourself
TEXT
--- WhenAll complete ---
SiteA loaded
SiteB loaded
SiteC loaded
First completed: SiteA loaded

Async Method Naming Convention

Async method names should use the Async suffix. This is a .NET convention that makes it easy to distinguish between synchronous and asynchronous methods.

Rules:

Example

CSHARP
using System;
using System.Threading.Tasks;

async Task<string> ReadFileAsync(string path)
{
    await Task.Delay(200);
    return "Read: " + path;
}

async Task SaveDataAsync(string data)
{
    await Task.Delay(100);
    Console.WriteLine("Saved: " + data);
}

string content = await ReadFileAsync("/data.txt");
await SaveDataAsync(content);
▶ Try it Yourself
TEXT
Saved: Read: /data.txt

CancellationToken Cancellation Mechanism

CancellationToken is used for cooperative cancellation of asynchronous operations. The caller sends a cancellation signal through CancellationTokenSource, and the callee checks the token and exits early.

Usage steps:

  1. Create a CancellationTokenSource
  2. Pass cts.Token to the async method
  3. The async method periodically checks token.IsCancellationRequested or passes it to cancellable APIs
  4. Call cts.Cancel() when cancellation is needed

Example

CSHARP
using System;
using System.Threading;
using System.Threading.Tasks;

async Task LongWorkAsync(CancellationToken token)
{
    for (int i = 0; i < 10; i++)
    {
        token.ThrowIfCancellationRequested();
        Console.WriteLine("Step " + (i + 1));
        await Task.Delay(300, token);
    }
    Console.WriteLine("All done");
}

using var cts = new CancellationTokenSource();
cts.CancelAfter(1200);

try
{
    await LongWorkAsync(cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation cancelled");
}
▶ Try it Yourself
TEXT
Step 1
Step 2
Step 3
Step 4
Operation cancelled

IAsyncEnumerable<T> Async Streams

C# 8 introduced IAsyncEnumerable<T>, supporting asynchronous generation and consumption of data streams. Use await foreach to iterate items asynchronously, suitable for scenarios like batch database reads and streaming network responses.

Example

CSHARP
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

async IAsyncEnumerable<int> GenerateNumbersAsync()
{
    for (int i = 1; i <= 5; i++)
    {
        await Task.Delay(200);
        yield return i;
    }
}

await foreach (int num in GenerateNumbersAsync())
{
    Console.WriteLine("Received: " + num);
}
▶ Try it Yourself
TEXT
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5

Common Async Pitfalls

⚠️ Avoid async void

async void cannot be awaited, exceptions cannot be caught, and it can cause application crashes. Only use it in event handlers.

⚠️ Avoid using .Result and .Wait()

Calling .Result or .Wait() in a UI or ASP.NET context can cause a deadlock: the async method waits for the caller to release the thread, while the caller waits for the async method to complete — both sides wait for each other.

⚠️ ConfigureAwait(false)

Library code should use ConfigureAwait(false) to avoid capturing the synchronization context, preventing deadlocks and improving performance.

Example

CSHARP
using System;
using System.Threading.Tasks;

async Task<string> BadMethodAsync()
{
    await Task.Delay(100);
    return "Done";
}

async Task SafeUsageAsync()
{
    string result = await BadMethodAsync();
    Console.WriteLine("Correct usage: " + result);
}

async Task LibraryMethodAsync()
{
    string result = await BadMethodAsync().ConfigureAwait(false);
    Console.WriteLine("Library usage: " + result);
}

await SafeUsageAsync();
await LibraryMethodAsync();
▶ Try it Yourself
TEXT
Correct usage: Done
Library usage: Done

❓ FAQ

Q What is the difference between async void and async Task?
A async void cannot be awaited and exceptions cannot be caught; it should only be used for event handlers. async Task is the standard approach — it can be awaited and exceptions can be handled.
Q When should I use Task.Run?
A Only for CPU-intensive compute work. I/O operations should use native async APIs (like ReadAsync), not wrapped with Task.Run.
Q What problems can .Result cause?
A In UI/ASP.NET synchronization contexts, it can deadlock — the calling thread waits for the Task to complete, while the Task waits for the synchronization context to be released, causing mutual blocking.
Q When should I use ConfigureAwait(false)?
A Use it in library code to avoid capturing the synchronization context, preventing deadlocks and improving performance. UI/ASP.NET application-layer code generally doesn't need it.

📖 Summary

📝 Exercises

  1. Write an async method DownloadAllAsync that uses Task.WhenAll to download 3 simulated data sources concurrently and returns an array of all results
  2. Write an async countdown method that supports CancellationToken, outputs remaining seconds each second, and prints "Countdown cancelled" when cancelled
  3. Use IAsyncEnumerable<int> to generate the first 20 Fibonacci numbers, iterate with await foreach, and output each
  4. Identify the problem in the following code and fix it: async void Button_Click() { var data = GetDataAsync().Result; Console.WriteLine(data); }
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏