Question
I am trying to understand how async and await work in C#.
From what I understand, one of their main benefits is making asynchronous code easier to write and read. However, does using async and await mean that long-running work is automatically moved to a background thread?
I am testing this simple example and added comments to explain what confuses me:
private async void button1_Click(object sender, EventArgs e)
{
Task<int> access = DoSomethingAsync();
// Task-independent work here
// Why is this line only reached after the 5-second delay in
// DoSomethingAsync()? Shouldn't it run immediately?
int a = 1;
// I thought the waiting would happen here.
int x = await access;
}
async Task<int> DoSomethingAsync()
{
// Is this executed on a background thread?
System.Threading.Thread.Sleep(5000);
return 1;
}
Can you explain:
- why
button1_Clickmust be markedasync - whether
DoSomethingAsync()runs on a background thread - why execution does not continue immediately after calling
DoSomethingAsync() - what
awaitis actually doing in this example
Short Answer
By the end of this page, you will understand that async and await in C# do not automatically create background threads. You will learn what async really means, what await pauses, why Thread.Sleep() blocks execution, and how to write truly asynchronous code using APIs like Task.Delay() instead of blocking calls.
Concept
What async and await really do
In C#, async and await are language features for working with asynchronous operations. They help you write code that looks sequential while allowing the program to avoid blocking the current thread when possible.
A very common beginner misunderstanding is this:
- Myth:
asyncautomatically runs code on another thread. - Reality:
asyncby itself does not start a new thread.
What async does
Marking a method with async allows you to use await inside it. That is its main purpose.
For example:
async Task<int> GetValueAsync()
{
int value = await SomeOperationAsync();
return value;
}
Without async, you cannot use inside the method body.
Mental Model
Think of async/await like ordering food at a restaurant.
- Synchronous code: You stand at the counter and refuse to move until your food is ready. Nobody else can use your spot.
- Asynchronous code: You place your order, get a buzzer, and step aside. When the food is ready, the buzzer tells you to come back.
Now apply that to your code:
Taskis the buzzer ticket.awaitis stepping aside until the food is ready.Thread.Sleep()is standing still at the counter for 5 seconds and blocking everyone behind you.
So await is not “make a new worker.” It is more like “pause here and come back later when the work finishes.”
If the work itself is just blocking the current thread, like Thread.Sleep(), nothing is really gained.
Syntax and Examples
Basic syntax
async Task MyMethodAsync()
{
await SomeTaskReturningMethodAsync();
}
Methods that commonly use async
async Task DoWorkAsync()
{
await Task.Delay(1000);
}
async Task<int> GetNumberAsync()
{
await Task.Delay(1000);
return 42;
}
async void button1_Click(object sender, EventArgs e)
{
await DoWorkAsync();
}
Return type guide
async Task→ async method with no resultasync Task<T>→ async method that returns a valueasync void→ usually only for event handlers
Your original example fixed
Step by Step Execution
Example to trace
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("Click start");
Task<int> task = DoSomethingAsync();
Console.WriteLine("After calling DoSomethingAsync");
int x = await task;
Console.WriteLine($"Result: {x}");
}
async Task<int> DoSomethingAsync()
{
Console.WriteLine("DoSomethingAsync started");
await Task.Delay(2000);
Console.WriteLine("DoSomethingAsync finished");
return 1;
}
What happens step by step
- The button is clicked.
button1_Clickstarts running on the UI thread.Console.WriteLine("Click start")runs.DoSomethingAsync()is called.- Inside
DoSomethingAsync,Console.WriteLine("DoSomethingAsync started")runs immediately. - The method reaches .
Real World Use Cases
Where async/await is useful
Web requests
var response = await httpClient.GetAsync(url);
The app can do other work while waiting for the server.
Reading files
string text = await File.ReadAllTextAsync(path);
Useful when loading large files without freezing a UI.
Database queries
var user = await dbContext.Users.FindAsync(id);
Common in ASP.NET and desktop apps.
UI event handlers
private async void loadButton_Click(object sender, EventArgs e)
{
statusLabel.Text = "Loading...";
var data = await LoadDataAsync();
statusLabel.Text = "Done";
}
The UI stays responsive while data loads.
Real Codebase Usage
How this concept is used in real projects
Async all the way
If one method awaits another, the calling method often becomes async too.
public async Task SaveAsync()
{
await repository.SaveAsync();
}
This avoids blocking with .Result or .Wait().
Event handlers use async void
In UI frameworks, event handlers are one of the few correct places for async void.
private async void submitButton_Click(object sender, EventArgs e)
{
await SubmitFormAsync();
}
Guard clauses before awaiting
public async Task<string> LoadUserAsync(int id)
{
if (id <= )
ArgumentException();
api.GetUserAsync(id);
}
Common Mistakes
1. Thinking async creates a new thread
Broken assumption:
async Task DoWorkAsync()
{
Thread.Sleep(5000);
}
Why it is wrong:
asyncdoes not move this to another threadThread.Sleepstill blocks the current thread
Better:
async Task DoWorkAsync()
{
await Task.Delay(5000);
}
2. Marking a method async without await
async Task<int> GetNumberAsync()
{
return 1;
}
This compiles with a warning and is usually unnecessary.
Better:
Task<> ()
{
Task.FromResult();
}
Comparisons
Related concepts compared
| Concept | What it does | Blocks thread? | Creates new thread automatically? | Best for |
|---|---|---|---|---|
async | Allows await in a method | No, by itself | No | Writing async methods |
await | Pauses method until task completes | No | No | Waiting for async operations |
Thread.Sleep() | Stops current thread for a time | Yes | No | Rarely useful in async code |
Task.Delay() | Completes a task after a time |
Cheat Sheet
Quick rules
asynclets you useawaitawaitpauses the method, not the whole programasyncdoes not automatically create a new thread- An async method runs synchronously until its first incomplete
await Thread.Sleep()blocks the current threadTask.Delay()is the async, non-blocking alternative- Use
async Taskorasync Task<T>for most async methods - Use
async voidmainly for event handlers - Avoid
.Wait()and.Resultwhen possible
Common patterns
async Task DoWorkAsync()
{
await Task.Delay(1000);
}
async Task<int> ()
{
Task.Delay();
;
}
FAQ
Why does code before await still run immediately?
Because an async method starts executing normally. It only pauses when it reaches an incomplete awaited task.
Does await create a new thread?
No. await waits for a task to finish without blocking the current thread. It does not itself create threads.
Why did int a = 1; not run immediately in the example?
Because DoSomethingAsync() used Thread.Sleep(5000), which blocked the current thread before returning control.
Why must button1_Click be marked async?
Because it uses await access;. A method must be marked async if it contains await.
Is async void bad?
Usually yes for normal methods, but it is appropriate for event handlers in UI code.
What should I use instead of Thread.Sleep() in async code?
Use await Task.Delay(...) for delays, or use truly asynchronous APIs for I/O operations.
Mini Project
Description
Build a small desktop-style simulation that shows the difference between blocking and non-blocking delays. The project demonstrates why Thread.Sleep() freezes progress while await Task.Delay() allows the program to remain responsive.
Goal
Create a simple C# program that starts an asynchronous operation, does other work immediately, and then awaits the final result.
Requirements
- Create a method that returns
Task<int>after a non-blocking delay. - Call the method and store the returned task before awaiting it.
- Print or log a message immediately after starting the task.
- Await the task later and print the returned result.
- Do not use
Thread.Sleep()inside the asynchronous method.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
C# Type Checking Explained: typeof vs GetType() vs is
Learn when to use typeof, GetType(), and is in C#. Understand exact type checks, inheritance, and safe type testing clearly.
C# Version Numbers Explained: C# vs .NET Framework and Why “C# 3.5” Is Incorrect
Learn the correct C# version numbers, how they map to .NET releases, and why terms like C# 3.5 are inaccurate and confusing.