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 CancellationTokenparameter to the method and then I've passed it to theTask.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.Delaythen we shallow theOperationCanceledExceptionand 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 RandomStringright away instead it returns a function which expects aCancellationToken.
- Whenever a CancellationTokenis 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- CancellationTokento return a- Task<T>.
- exitCondition: As its name suggests...
- cts: The- CancellationTokenprovider.
- First we start the async methods by passing the CancellationTokento them.
- Inside the loop we wait for the fastestto complete.
- If the fastest's result is equal to theexitConditionthen we cancel the remaining tasks.
- If they are not equal then we remove the completed job from the jobsand 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