HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

Returning the result of whichever method finishes first

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
resultfinishesthewhichevermethodreturningfirst

Problem

Consider a method (let's call it IsPrimeHybrid) which calls at least two methods (let's call them IsPrimeNaive and IsPrimeBySearch) that calculate an identical result using different approaches and the outer method returns the result when any of the inner methods finishes.

The following is my first attempt to write such a method, but I'd like to hear what issues it may have and what's a good way to fix them. (For example, how to implement thread safety in a scenario like this.)

public static bool IsPrimeHybrid(BigInteger number)
    {
        var result = default(bool);
        var byPureCalc = Task.Run(() => result = IsPrimeNaive(number));
        var bySearchFirst = Task.Run(() => result = IsPrimeBySearch(number));
        Task.WaitAny(byPureCalc, bySearchFirst);
        return result;
    }


For the sake of simplicity, please assume that none of the called methods will consume any common resources, apart from processing power and memory (that is, there will never be two methods that both use file I/O or network I/O, etc).

Solution

Tasks can have results, you should take advantage of that, instead of assigning a local variable from a lambda. And WaitAny() returns the index of the Task that finished first. This means you can do something like:

public static bool IsPrimeHybrid(BigInteger number)
{
    var byPureCalc = Task.Run(() => IsPrimeNaive(number));
    var bySearchFirst = Task.Run(() => IsPrimeBySearch(number));
    var tasks = new[] { byPureCalc, bySearchFirst };
    int firstIndex = Task.WaitAny(tasks);
    return tasks[firstIndex].Result;
}


Also, CPU and memory are also resources that you shouldn't waste. So, you may want to cancel the slower Task, after the faster one finished. For that, you can use CancellationToken, which the two computations will periodically check (you can use ThrowIfCancellationRequested () for that):

public static bool IsPrimeHybrid(BigInteger number)
{
    var cts = new CancellationTokenSource();

    var byPureCalc = Task.Run(() => IsPrimeNaive(number, cts.Token));
    var bySearchFirst = Task.Run(() => IsPrimeBySearch(number, cts.Token));
    var tasks = new[] { byPureCalc, bySearchFirst };

    int firstIndex = Task.WaitAny(tasks);

    cts.Cancel();
    return tasks[firstIndex].Result;
}

Code Snippets

public static bool IsPrimeHybrid(BigInteger number)
{
    var byPureCalc = Task.Run(() => IsPrimeNaive(number));
    var bySearchFirst = Task.Run(() => IsPrimeBySearch(number));
    var tasks = new[] { byPureCalc, bySearchFirst };
    int firstIndex = Task.WaitAny(tasks);
    return tasks[firstIndex].Result;
}
public static bool IsPrimeHybrid(BigInteger number)
{
    var cts = new CancellationTokenSource();

    var byPureCalc = Task.Run(() => IsPrimeNaive(number, cts.Token));
    var bySearchFirst = Task.Run(() => IsPrimeBySearch(number, cts.Token));
    var tasks = new[] { byPureCalc, bySearchFirst };

    int firstIndex = Task.WaitAny(tasks);

    cts.Cancel();
    return tasks[firstIndex].Result;
}

Context

StackExchange Code Review Q#46794, answer score: 14

Revisions (0)

No revisions yet.