Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Storing" a task for later completion

I'm trying to "store" an async task for later completion - I've found the async cache example but this is effectively caching task results in a concurrent dictionary so that their results can be reloaded without re-doing the task again (the HTML implementation is here).

Basically what I'm trying to design is a dictionary of tasks, with correlation IDs (GUIDs) as the key. This is for co-ordinating incoming results from another place (XML identified by the GUID correlation ID) and my aim is for the task to suspend execution until the results come in (probably from a queue).

Is this going to work? This is my first foray into proper async coding and I can't find anything similar to my hopeful solution so I may well be entirely on the right track.

Can I effectively "store" a task for later completion, with the task result being set at completion time?

Edit: I've just found out about TaskCompletionSource (based on this question) is that viable?

like image 688
Matt Hogan-Jones Avatar asked Mar 02 '26 07:03

Matt Hogan-Jones


2 Answers

If I understand your use-case correctly, you can use TaskCompletionSource.

An example of implementation:

public class AsyncCache
{
    private Dictionary<Guid, Task<string>> _cache;

    public Task<string> GetAsync(Guid guid)
    {
        if (_cache.TryGetValue(guid, out var task))
        {
            // The value is either there or already queued
            return task;
        }

        var tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);

        _queue.Enqueue(() => {
           var result = LoadResult();
           tcs.TrySetValue(result);
        });

        _cache.Add(guid, tcs.Task);

        return tcs.Task;            
    }
}

Here, _queue is whatever queuing mechanism you're going to use to process the data. Of course, you would also have to make that code thread-safe.

like image 128
Kevin Gosse Avatar answered Mar 03 '26 22:03

Kevin Gosse


Are you thinking of lazy loading? You could use Lazy<Task> (which will initialise the task but not queue it to run).

var tasks = new Dictionary<Guid, Lazy<Task>>();

tasks.Add(Task1Guid, new Lazy<Task>(() => { whatever the 1st task is }));
tasks.Add(Task2Guid, new Lazy<Task>(() => { whatever the 2nd task is }));


void async RunTaskAsync(Guid guid)
{
   await tasks[guid].Value;
}
like image 26
Neil Avatar answered Mar 03 '26 22:03

Neil