C#: Async vs Sync | Differences between Asynchronous and Synchronous Programming in C#

By Josip Miskovic
A thumbnail showing C# asnyc vs sync code.

C#: Async vs Sync

The main difference between async and sync programming in C# is that async allows for non-blocking code execution, while, sync blocks the calling thread until the called method returns. With async, the thread continues to execute other code while the called method is running.

Sync is simpler but can hurt performance, while async can improve performance but is more complex because it requires knowledge of threading, parallelism, and mental models.

The table below compares the differences between async vs sync in C#:

Synchronous Programming Asynchronous Programming
Blocking Non-blocking
Single threaded Can be multi-threaded
Simple to understand Complex
May result in UI freezes Can improve UI responsiveness
Limited to the capabilities of a single CPU Can take advantage of multiple CPUs or CPU cores
Exceptions are raised immediately Exceptions can be wrapped in a task and raised later
Does not use tasks Uses tasks to represent async unit of work

A thread is an independent path of execution within a process, which provides an isolated environment for a program to run. In a single-threaded program, one thread has exclusive access to the process's environment. In a multi-threaded program, multiple threads share the same environment and can access shared data, allowing for tasks to be performed simultaneously.

In this video, an explanation was given on how the thread pool and asynchronous tasks work together to achieve parallelism in code. The thread pool manages the threads and hands them off to the operating system which then utilizes the processor to schedule them. Threads are not bound to a particular CPU but can jump between CPUs while executing their tasks. Asynchronous tasks are the bridge between state machines and code, spawning the state machine and executing the third part. Understanding how the thread pool and asynchronous tasks work together is key to unlocking their potential.

When to use async over sync?

Asynchronous programming is used when you want to perform lengthy or resource-intensive operations without blocking the main thread or UI of the application.

Here are some examples of when you might want to use async in C#:

I/O-bound or CPU-bound operations

Async programming allows concurrent I/O or CPU operations with the rest of the program, improving performance and responsiveness. Thus, you can use async over sync for reading a file, long database queries, and downloading large files.

UI applications

It is important to keep UI applications responsive to user input. Use async programming over sync to run resource-intensive operations in the background, without blocking the main thread or UI, improving the application's responsiveness and usability.

Handling cancellation

Asynchronous programming can also be used to handle cancellation or timeouts, by using the CancellationToken and Timeout classes. Using CancellationToken is useful in scenarios where you want to allow the user to cancel a long-running operation or to enforce a time limit on an operation.

Handling multiple requests concurrently

In ASP.NET API applications, you can use asynchronous programming to handle multiple requests concurrently. By using async, you can free up resources quicker to process multiple requests simultaneously, rather than processing them sequentially.

Async vs Sync: Performance

Async code does not directly make the code execution faster, but it does require fewer threads.

For example, if your application has a lot of concurrent requests, a synchronous process could lead to resource exhaustion. However, the database would be overwhelmed before this happens. Async data access is not designed to improve the performance of data access, but to meet the needs of application frameworks that prefer Async IO, such as desktop applications with a single UI thread or web applications using ASP.NET Core.

FAQ: Async vs Sync

When was async/await introduced?

The async and await keywords were introduced in C# 5.0, which was released in 2012 as part of the .NET Framework 4.5.

The async and await keywords were added to C# to make it easier to write asynchronous code. Prior to their introduction, asynchronous programming in C# was typically done using the Task and Task<T> types from the System.Threading.Tasks namespace, along with the BeginInvoke and EndInvoke methods of the Delegate class.

The async and await keywords simplify asynchronous programming in C# by allowing you to use a synchronous-style programming model, while still allowing the code to be executed asynchronously. This can make it easier to write and understand asynchronous code, while still taking advantage of the performance benefits of concurrent execution.

Async functions revolutionized the .NET world because they enable responsive and thread-safe rich-client apps, as well as efficient and concurrent I/O-bound apps that don't use up thread resources.

What is Task?

The Task class is a wrapper for a unit of work that can be executed asynchronously. It's a central part of the Task-based Asynchronous Pattern (TAP) in C#, which provides a way to write asynchronous code using tasks and asynchronous methods.

The Task class uses threads, thread pools, and synchronization objects to run. When you create a Task, the TPL schedules it to run on a thread from the thread pool, which is a collection of worker threads managed by the TPL to optimize multi-threading.

Task class is part of the System.Threading.Tasks namespace.

Josip Miskovic
About Josip

Josip Miskovic is a software developer at Americaneagle.com. Josip has 10+ years in experience in developing web applications, mobile apps, and games.

Read more posts →
Published on:
Download Free Software Developer Career Guide

I've used these principles to increase my earnings by 63% in two years. So can you.

Dive into my 7 actionable steps to elevate your career.