C# Program Flow

C# Program Flow

Both single-threaded and multi-threaded programming is relevant in web development, depending on the specific use case and requirements.

In single-threaded web applications, the web server handles one request at a time, in a sequential manner. This approach is simpler and easier to reason about since there are no concurrency issues to worry about. Single-threaded programming is often used for simple web applications or for small-scale internal applications that don't require high levels of concurrency or scalability.

Now, Think of a recipe for your favorite food, you list the ingredients and then follow a sequence of steps to prepare a delicious dish, perhaps you go from step 1 to the final step (single Thread) or maybe you have to prepare the sauce while cooking the meat (multi-Thread), you need, then, two separate actions running simultaneously, that sequence of steps is the program flow for your recipe.

For more complex web applications that need to handle a high volume of requests, multi-threaded programming is often used. By handling requests concurrently using multiple threads, a web application can improve its performance and scale to handle more requests. For example, a multi-threaded web application might use a thread pool to handle incoming requests, allowing the webserver to handle multiple requests simultaneously., here is an example:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;

namespace MyWebApp.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class MyController : ControllerBase
    {
        [HttpGet("{id}")]
        public async Task<IActionResult> GetAsync(int id)
        {
            Console.WriteLine("Starting request " + id);

            // Start a new thread to perform some long-running operation
            await Task.Run(() =>
            {
                Console.WriteLine("Thread " + id + " is running...");
                // Do some long-running operation here
            });

            Console.WriteLine("Request " + id + " completed");

            return Ok("Request " + id + " completed");
        }
    }
}

Note that in a real-world application, you would typically use multi-threading to perform more complex and time-consuming tasks, such as processing large amounts of data or communicating with external systems. You should also be careful to ensure that your multi-threaded code is thread-safe and does not cause race conditions or other concurrency issues.

In this example, we have a simple controller with a single GET endpoint that takes an ID parameter. When the endpoint is called, the controller logs a message indicating that the request is starting, starts a new thread to perform some long-running operation, and then logs another message indicating that the request has been completed.

The Task.Run method is used to start a new thread that runs the long-running operation. This method returns a Task object that represents the operation being performed on the new thread. The await keyword is used to asynchronously wait for the task to complete before returning the HTTP response.

Multi-Threading With Tasks in C

Now, Instead of using one Thread, you can make use of a Thread Pool. ThreadPool Class, Provides a pool of threads that can be used to execute tasks, post work items, process asynchronous I/O, wait on behalf of other threads, and process timers.

docs.microsoft

A Task class represents a single and usually asynchronous operation

using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;

namespace MyWebApp.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class MyController : ControllerBase
    {
        [HttpGet("{id}")]
        public async Task<IActionResult> GetAsync(int id)
        {
            Console.WriteLine("Starting request " + id);

            // Queue a work item to the thread pool to perform some long-running operation
            await Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Thread " + id + " is running...");
                // Do some long-running operation here
            }, TaskCreationOptions.LongRunning);

            Console.WriteLine("Request " + id + " completed");

            return Ok("Request " + id + " completed");
        }
    }
}

In this example, we're using the Task.Factory.StartNew method to queue a work item to the thread pool. The TaskCreationOptions.LongRunning flag is used to indicate that the task may take a long time to complete, so the thread pool should allocate a dedicated thread for it.

When the endpoint is called, the controller logs a message indicating that the request is starting, queues a work item to the thread pool to perform a long-running operation, and then logs another message indicating that the request has been completed.

Using a thread pool allows the webserver to handle multiple requests concurrently, without creating a new thread for each request, which could cause performance issues and resource exhaustion. Instead, the thread pool manages a set of threads that can be reused to handle multiple requests over time.

Did you find this article valuable?

Support Nestor Rojas by becoming a sponsor. Any amount is appreciated!