Putting Lazy Tasks in a Cache, and Computing Only Once, When First Requested

This post is an update to an earlier post on caching Tasks and only executing them when first accessed. In that post, I used Task.Start() to start the task when it was first accessed. This works, but there is another way, as suggested by Steven Cleary in the comments on that post.

It stores Lazy<Task<string>> in the cache, and when the value is first accessed, the Value property of the Lazy<T> is accessed, which starts the task.

 1using Microsoft.Extensions.Caching.Memory;
 2
 3var builder = WebApplication.CreateBuilder(args);
 4var app = builder.Build();
 5
 6MemoryCache cache = new MemoryCache(new MemoryCacheOptions());
 7
 8for (int loop = 1; loop <= 20; loop++)
 9{
10    int delay = loop; // this is important
11    Lazy<Task<string>> taskToRunLater = new Lazy<Task<string>>(() => DoWorkAsync(delay));
12    cache.Set(loop, taskToRunLater);
13}
14
15app.MapGet("/{id}", async (int id) => {
16    if(cache.TryGetValue(id, out Lazy<Task<string>>? doWorkAsyncTask) && doWorkAsyncTask != null)
17    {
18        return await doWorkAsyncTask.Value;
19    }
20    return $"Value {id} not found";
21});
22
23app.Run();
24
25async Task<string> DoWorkAsync(int number)
26{
27    Console.WriteLine($"Running DoWorkAsync with a {number} second delay");
28    DateTime started = DateTime.Now; 
29    await Task.Delay(number * 1000);
30    return $"Started at {started:HH:MM:ss}, finished at {DateTime.Now:HH:MM:ss}. This value took {number} seconds to generate";
31}
  • line 11 creates a Lazy<Task<string>> that will run the lambda that calls DoWorkAsync(delay) when the taskToRunLater.Value property is accessed.
  • line 12 stores the taskToRunLater in the cache.
  • line 16 retrieves the Lazy<Task<string>> from the cache.
  • line 18 accesses the Value property of the Lazy<Task<string>>, which starts the task if it has not already been started, and returns the Task<string>. If the task has already completed, it just returns the Task<string>.

That’s it, most of the code remains the same as the earlier post.

comments powered by Disqus

Related