Your provided example won't compile, because you can't store Task<bool>
in a collection of Task<string>
.
If I understand your question properly then you are looking for a solution:
- To start multiple methods simultaneously
- Stop the execution whenever one of the methods return with a given value.
In order to implement such a solution we have to use CancellationToken
to be able to stop any ongoing operation. So the amended RandomString
could look like this:
public static async Task<string> RandomString(int delay, CancellationToken token)
{
try
{
await Task.Delay(delay, token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Task has been cancelled " + delay);
return null;
}
Console.WriteLine("Finished " + delay);
return "Random " + delay;
}
- I have added a new
CancellationToken
parameter to the method and then I've passed it to the Task.Delay
.
- If there was no cancellation then it will print out some debug information and return with some random string.
- If there was a cancellation during
Task.Delay
then we shallow the OperationCanceledException
and print out some debug info.
In order to be able to cold start this method, we could introduce a simple wrapper around it:
public static Func<CancellationToken, Task<string>> RandomStringWrapper(int delay)
=> (ct) => RandomString(delay, ct);
- It does not call the
RandomString
right away instead it returns a function which expects a CancellationToken
.
- Whenever a
CancellationToken
is passed then the Task will run.
So, now we can introduce an extension method which is generic enough to support multiple different async methods:
public static class TaskEx
{
public static async Task RunUntil<T>(this IEnumerable<Func<CancellationToken, Task<T>>> asyncFunctions, T exitCondition, CancellationTokenSource cts)
{
var jobs = asyncFunctions.Select(fn => fn(cts.Token)).ToList();
while (true)
{
var fastest = await Task.WhenAny(jobs);
if (fastest.Result.Equals(exitCondition))
{
cts.Cancel(true);
return;
}
jobs.Remove(fastest);
if (jobs.Count == 0)
return;
}
}
}
asyncFunctions
: It is a collection of functions. Each expects a CancellationToken
to return a Task<T>
.
exitCondition
: As its name suggests...
cts
: The CancellationToken
provider.
- First we start the async methods by passing the
CancellationToken
to them.
- Inside the loop we wait for the
fastest
to complete.
- If the
fastest
's result is equal to the exitCondition
then we cancel the remaining tasks.
- If they are not equal then we remove the completed job from the
jobs
and re-run the same loop until there is a job to execute.
Now, let's put all this together:
static async Task Main(string[] args)
{
var cts = new CancellationTokenSource();
var tasks = new[] { RandomStringWrapper(14), RandomStringWrapper(600), RandomStringWrapper(500) };
await tasks.RunUntil("Random 500", cts);
}
The output will be:
Finished 14
Finished 500
Task has been cancelled 600