Follow the recommended practices when working with asynchronous programming to achieve scalability and performance benefits You can take advantage of asynchronous programming to perform resource-intensive operations without having to block on executing thread of your application. This increases the throughput while at the same time ensuring that the user interface is responsive. However, you should be aware of the concepts of asynchrony, how it all actually works, and the best practices involved to avoid potential deadlocks and slow performance when using asynchrony the wrong way. This is a continuation of the series of my articles on the best practices on asynchronous programming. The key take away from this series of articles would be the best practices and strategies that can be followed to achieve scalability and performance benefits when working with asynchrony in .Net. Here’s the link to the first part in this series. Avoid creating create asynchronous wrappers with Task.Run method Let’s check out a use case. Consider the following piece of code that illustrates an asynchronous wrapper using the Task.Run method. public async Task<string> DownloadStringFromWebAsync(Uri uri) { return await Task.Run(() => { using (var webClient = new WebClient()) { return webClient.DownloadString(uri); } }); } Note that any method that is marked with the async keyword can have one or more await keywords inside. The “await” keyword is used in an async method to inform the compiler that the method can have a suspension and resumption point. The commonly used awaitable types are Task and Task. Well, this is an example of bad code! It would execute synchronously and would block on the background thread. What you’re doing here is just using a thread from the thread pool. However, you would be consuming more resources here and you would not get any scalability benefit at all. Remember that an asynchronous method should always have synchronous method signature and you should not wrap a synchronous method with the Task.Run method to achieve asynchrony — it would never ever help! If you would like to leverage the performance and scalability benefits of asynchrony, you should implement the asynchronous programming pattern using the async and await keywords as shown in the modified code snippet below. public async Task<string> DownloadStringFromWebModifiedAsync(Uri uri) { using (var httpClient = new HttpClient()) { return await httpClient.GetStringAsync(uri); } } The above code is asynchronous and wouldn’t block the current thread. Avoid using Task.Run in ASP.Net applications You should avoid using thread pool threads (other than the request thread that is given to you by the ASP.Net runtime) when working in ASP.Net applications. You would not gain any performance benefit by using the Task.Run method in ASP.Net as it would spawn a new thread. Refer to the code snippet given below. [HttpGet] public async Task<Author> GetAuthor(Author authorId) { return await Task.Run(() => GetAuthorById(authorId)); } Essentially, when using Task.Run in your Web API controller in this way, you would just be releasing one thread and blocking another. Creating additional threads in this way would increase the overhead actually. Hence, you should avoid using the Task.Run method in ASP.Net. If you are using Task.Run method in Web API, you are limiting scalability by passing the load to another thread from the thread pool unnecessarily. Incidentally, your Web API request already uses a ThreadPool thread. So, usage of Task.Run method is considered a bad practice when working with ASP.Net applications. Avoiding deadlocks If you are consuming a method that takes advantage of asynchronous programming features available in the TPL in a blocking manner, you might encounter a deadlock situation. In essence, a deadlock can occur if you are blocking on such a method if you are waiting for the Task to complete using Wait or trying to retrieve of the operation using the Result property of the Task object. You can take advantage of ConfigureAwait(false) to avoid deadlocks. Note that the moment your async method encounters the await operator, the method is blocked. However, the thread is returned to the thread pool and when your asynchronous method is ready to continue again, the runtime will pick up any thread from the thread pool to resume the call. The ConfigureAwait(false) method call is used to ensure that the Task continuation doesn’t resume on the captured context. In other words, this call ensures that the continuation will not execute on the caller’s context. Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos