Cache your Task objects to improve performance by preventing the unnecessary expensive operations from being executed Credit: IDG Caching is a state management strategy that long has been used in applications to store relatively stable and frequently accessed data for faster access. It helps you to boost your application’s performance primarily because you can save on the consumption of resources in your system to access the data from the cache. Caching can also make sure that your application gets the data it needs even when the data store is not available. So far so good. Now, we have used data caching many times in our applications for improving the application’s performance. But we can cache Task objects as well! Task caching Task caching is an approach towards caching in which instead of caching the results of the execution of a Task, you cache the Tasks instances themselves. In doing so, you can reduce the overhead of expensive operations each time a new Task instance is started. Now, what if the Task fails? Note that we should not cache failed or faulted Tasks, i.e., negative caching should not be considered. Let’s now dig into some code and understand how this can be implemented. Implementing Task caching The following method attempts to get or add data from the cache based on the key passed as a parameter. public static async Task<object> GetOrAddDataInCache(string key) { if (key == null) return Task.FromResult<object>(null); object result; if (!cache.TryGetValue(key, out result)) { result = await SomeMethodAsync(); cache.TryAdd(key, result); } return result; } The cache collection being used here is: static ConcurrentDictionary<string, object> cache = new ConcurrentDictionary<string, object>(); Now, in this example, we have cached the data, not the Task instance. The following method illustrates how Task caching can be achieved. public static async Task<object> GetOrAddTaskInCache(string key) { if (key == null) return Task.FromResult<object>(null); Task<object> result; if (!cache.TryGetValue(key, out result)) { result = await Task.FromResult(SomeMethodAsync()); cache.TryAdd(key, result); } return result; } Note how the Task object is added to the cache if it’s not available. If the Task instance is available in the cache, it is returned else, a call is made to the asynchronous method and the resultant Task instance is inserted in the cache. And, here’s the cache object for your reference. static ConcurrentDictionary<string, Task<object>> cache = new ConcurrentDictionary<string, Task<object>>(); Note that we are using a ConcurrentDictionary to store the cached data. You can also take advantage of other cache stores if you want to. The asynchronous method SomeMethodAsync can perform some long running operation. I leave it to my readers to change it as per the needs. Here’s a simple version of the method anyway for your reference. public static async Task<object> SomeMethodAsync() { //Write your code here for some long running operation await Task.Delay(100); //Simulates a delay of 100 milliseconds return "Hello World!"; } OK, but should I be caching the Task objects always? Absolutely not! Usually, the overhead of state machine allocation when creating a Task instance is negligible, but you can still take advantage of Task caching when you would need to perform relatively expensive operations in your applications often. This was just a simple implementation to illustrate how Task caching works. We didn’t consider many things here. You can refer to this nice post to read a more elaborate implementation on Task caching. 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