2014-07-03 38 views
9

我想向X个不同的Web服务提出请求,每个Web服务将返回truefalseTPL等待任务以特定返回值完成

这些任务应该并行执行,我想等待第一个完成真正的价值。当我收到真正的价值时,我不希望等待其他任务完成。

在下面的例子中,t1不应该等待因为t3完成第一和返回true

var t1 = Task.Run<bool>(() => 
{ 
    Thread.Sleep(5000); 
    Console.WriteLine("Task 1 Excecuted"); 
    return true; 
}, cts.Token); 

var t2 = Task.Run<bool>(() => 
{ 
    Console.WriteLine("Task 2 Executed"); 
    return false; 
}, cts.Token); 

var t3 = Task.Run<bool>(() => 
{ 
    Thread.Sleep(2000); 
    Console.WriteLine("Task 3 Executed"); 
    return true; 
}, cts.Token); 

基本上我正在寻找Task.WhenAny有谓语,这当然是不存在的。

+1

http://stackoverflow.com/questions/14726854/task-parallel-library-waitany-with-specified-result –

+0

@AdamPlocher该问题在'async-await'环境中不太相关。 – i3arnon

+0

我在这里感受到TPL DataFlow解决方案,希望我有时间坐下来玩玩! –

回答

16

您可以简单地使用Task.WhenAny和谓语多次,直到“正确”的任务走来

async Task<T> WhenAny<T>(IEnumerable<Task<T>> tasks, Func<T, bool> predicate) 
{ 
    var taskList = tasks.ToList(); 
    Task<T> completedTask = null; 
    do 
    { 
     completedTask = await Task.WhenAny(taskList); 
     taskList.Remove(completedTask); 
    } while (!predicate(await completedTask) && taskList.Any()); 

    return completedTask == null ? default(T) : await completedTask; 
} 
8

一种选择是使用Reactive Extensions。让我们假设你有一组任务。这可能是你在问题中提到的任务:

var tasks = new[] { t1, t2, t3 }; 

并行执行的任务和返回时的首要任务返回你用这个表达true

var result = await tasks 
    .Select(t => t.ToObservable()) 
    .Merge() 
    .FirstOrDefaultAsync(success => success); 

的任务转化为可观察当任务完成时,每个“发射”一次。这些序列然后被合并成一个单一的序列,然后被“转换”回可以使用谓词等待的东西。如果需要,您可以使用更复杂的谓词而不是success => success

在这之后,你可以,如果你使用的是CancellationTokenSource取消剩余未完成的任务:

cts.Cancel(); 

变量result现在是要么truefalse和任何剩余的任务都被赋予了信号取消。

如果你想与样品的任务来测试这一点,你将不得不稍微修改他们使用Task.Delay,而不是Thread.Sleep使任务被取消:

var t1 = Task.Run<bool>(async() => 
{ 
    await Task.Delay(TimeSpan.FromSeconds(1), cts.Token); 
    Console.WriteLine("Task 1 Excecuted"); 
    return false; 
}, cts.Token);