Synchronous and Asynchronous Methods in C# / ASP.
NET Core API
Table of Contents
1. Introduction
2. Synchronous Methods
1. Definition
2. Use Cases
3. Synchronous Workflow
4. Example of Synchronous Method (C# / ASP.NET Core)
3. Asynchronous Methods
1. Definition
2. Use Cases
3. Asynchronous Workflow
4. Example of Asynchronous Method (C# / ASP.NET Core)
4. Synchronous vs. Asynchronous: Key Differences
5. Workflow Diagram: Synchronous vs. Asynchronous
5.1. Sync vs Async in RAM and CPU
6. Real-World Example: MVC, Dependency Injection, Repository Pattern, and CRUD
Features
1. Project Structure
2. Controller Example: Synchronous vs. Asynchronous
3. Service Layer: Dependency Injection
4. Repository Layer
7. Conclusion
1. Introduction
In modern software development, understanding the differences between synchronous and
asynchronous programming is crucial for optimizing the performance and scalability of
applications. This guideline will explore both methods, their use cases, workflow, and practical
examples using C# and ASP.NET Core API projects with the MVC design pattern, dependency
injection, repository pattern, and CRUD operations.
2. Synchronous Methods
2.1. Definition
A synchronous method is one where operations are executed sequentially. Each task waits for
the previous one to complete before proceeding, which can lead to performance bottlenecks
when performing tasks such as database access, file I/O, or network calls.
2.2. Use Cases
● Simple tasks where performance isn't a critical factor.
● Scenarios where code execution needs to follow a strict order, ensuring that one
operation is fully complete before the next starts.
2.3. Synchronous Workflow
In synchronous methods, each step in the code must wait for the previous step to finish before
moving forward, causing potential delays, especially in I/O-bound operations
● Thread : A thread is the smallest unit of execution in a process that can perform tasks
independently in CPU and Memory.
Thread Lifecycle:
● New: The thread is created but not yet started.
● Runnable: The thread is ready to run and waiting for the CPU scheduler to allocate CPU
time.
● Blocked/Waiting: The thread is waiting for some external event (e.g., I/O completion)
before it can proceed.
● Running: The thread is actively executing its task.
● Terminated: The thread has completed execution or has been forcefully stopped.
Use Case of Threads in Programming:
● Asynchronous programming often uses threads to handle tasks like I/O-bound
operations without blocking the main thread. This allows the application to remain
responsive while tasks like reading files, database queries, or API calls are executed in
the background.
2.4. Example of Synchronous Method (C# / ASP.NET Core)
public IActionResult GetProducts()
{
var products = _productService.GetAllProducts(); // Blocking call
return Ok(products);
}
In this synchronous example, the method blocks until the GetAllProducts service call
completes.
3. Asynchronous Methods
3.1. Definition
An asynchronous method allows tasks to run concurrently without blocking the main execution
thread. It enhances application responsiveness, particularly for I/O-bound operations, such as
accessing databases or external APIs.
3.2. Use Cases
● When handling I/O-bound tasks (database operations, API calls).
● In situations where you want to maintain responsiveness while waiting for lengthy
operations to complete.
3.3. Asynchronous Workflow
In asynchronous methods, tasks can be initiated and the program can continue executing other
tasks while waiting for the result, thus improving performance, especially in I/O-heavy scenarios.
3.4. Example of Asynchronous Method (C# / ASP.NET Core)
public async Task<IActionResult> GetProductsAsync()
{
var products = await _productService.GetAllProductsAsync(); // Non-blocking call
return Ok(products);
}
This example uses the async and await keywords to execute the GetAllProductsAsync
method without blocking the calling thread.
4. Synchronous vs. Asynchronous: Key Differences
Aspect Synchronous Asynchronous
Execution Flow Sequential, blocking Concurrent, non-blocking
Performance Can cause bottlenecks More efficient for I/O-bound operations
Complexity Simpler to implement Requires async and await constructs
Thread Utilization Holds threads while waiting Frees threads for other tasks
5. Workflow Diagram: Synchronous vs. Asynchronous
Below is a simplified workflow diagram contrasting synchronous and asynchronous processing:
● Synchronous Workflow:
1. Task A → waits until completed.
2. Task B → waits until Task A finishes.
3. Task C → waits until Task B finishes.
● Asynchronous Workflow:
1. Task A → starts and allows other tasks to execute.
2. Task B → executes while Task A is still running.
3. Task C → executes without waiting for Task A or B to finish.
5.1 Sync vs Async in RAM and CPU
● In a synchronous method, each request consumes a separate thread, which is
blocked while waiting for I/O, leading to higher RAM consumption and CPU
underutilization. As requests increase, server performance degrades.
● In an asynchronous method, threads are used efficiently. Instead of being blocked,
they are freed up during I/O waits, reducing RAM consumption and improving CPU
utilization, allowing the server to handle far more concurrent requests efficiently.
5.2 Queuing Mechanisms:
ASP.NET Core and Kestrel:
● Kestrel Server: In ASP.NET Core, the Kestrel web server handles incoming HTTP
requests. When Kestrel receives more requests than there are available threads, it
places the requests into an internal queue.
● Request Queue: This queue is managed within the server infrastructure, holding
requests until threads become available.
Thread Pool Queue:
● Work Queue: The .NET thread pool uses a work queue to handle tasks and requests. If
the thread pool is at capacity, new tasks or requests are placed into this queue.
6. Real-World Example: MVC, DI, Repository Pattern, and CRUD
6.1. Project Structure
- Controllers
- ProductsController.cs
- Services
- ProductService.cs
- Repositories
- ProductRepository.cs
- Models
- Product.cs
6.2. Controller Example: Synchronous vs. Asynchronous
Synchronous Version:
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet]
public IActionResult GetAll()
{
var products = _productService.GetAll();
return Ok(products);
}
}
Asynchronous Version:
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet]
public async Task<IActionResult> GetAllAsync()
{
var products = await _productService.GetAllAsync();
return Ok(products);
}
}
6.3. Service Layer: Dependency Injection
public class ProductService : IProductService
{
private readonly IProductRepository _productRepository;
public ProductService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task<IEnumerable<Product>> GetAllAsync()
{
return await _productRepository.GetAllAsync();
}
}
6.4. Repository Layer
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
public async Task<IEnumerable<Product>> GetAllAsync() {
await _context.Products.ToListAsync();
}
}
7. Conclusion
Synchronous and asynchronous methods both have distinct use cases, especially in modern
web applications that rely on handling multiple requests and responses efficiently. In C# /
ASP.NET Core, the combination of asynchronous programming, MVC architecture, dependency
injection, and repository patterns enables developers to write scalable, maintainable, and
performant applications.