2012-08-06 61 views
1

我有一些任务返回一个布尔值。我只想等到任何任务首先返回True时。可能吗 ?如何用条件等待任务?

我的第一个想法是使用CancellationTokenSource,但它不是一个好主意,因为它在我调用Task.WaitAll方法时引发异常。

第二个选择是使用bool我传入参考,如果它是真的,直接返回。它的工作原理,但它不是高性能:

bool isFound = false; 
Task<bool> t0 = Task.Factory.StartNew<bool>(() => Find(paramA, paramB, ref isFound)); 
Task<bool> t1 = Task.Factory.StartNew<bool>(() => Find(paramC, paramD, ref isFound)); 
Task<bool> t2 = Task.Factory.StartNew<bool>(() => Find(paramE, paramF, ref isFound)); 
Task<bool> t3 = Task.Factory.StartNew<bool>(() => Find(paramG, paramH, ref isFound)); 

Task.WaitAll(new Task[] { t0, t1, t2, t3, t4 }); 
return t0.Result | t1.Result | t2.Result | t3.Result | t4.Result; 

和方法:

private static bool Find(int[,] m1, int[,] m2, ref bool isFound) 
{ 
     if (isFound) 
      return false; 

     // Do work... 
} 

编辑:

的答案preconized,我用一个TaskCompletionSource<bool>现在:

TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); 
Task<bool> t0 = Task.Factory.StartNew<bool>(() => Find(paramA, paramB); 
Task<bool> t1 = Task.Factory.StartNew<bool>(() => Find(paramC, paramD); 
Task<bool> t2 = Task.Factory.StartNew<bool>(() => Find(paramE, paramF); 
Task<bool> t3 = Task.Factory.StartNew<bool>(() => Find(paramG, paramH); 

t0.ContinueWith(_ => 
{ 
    if (t0.Result) 
     tcs.TrySetResult(t0.Result); 
}); 

t1.ContinueWith(_ => 
{ 
    if (t1.Result) 
     tcs.TrySetResult(t1.Result); 
}); 

t2.ContinueWith(_ => 
{ 
    if (t2.Result) 
     tcs.TrySetResult(t2.Result); 
}); 

t3.ContinueWith(_ => 
{ 
    if (t3.Result) 
     tcs.TrySetResult(t3.Result); 
}); 

t4.ContinueWith(_ => 
{ 
    if (t4.Result) 
     tcs.TrySetResult(t4.Result); 
}); 

tcs.Task.Wait(); 
return tcs.Task.Result; 

在这种情况下,当所有任务返回false时,什么都不会被察觉,这是正常的。但是我不知道如何使用WhenAll方法。我想补充一点:

tcs.Task.Wait(); 
Task tr = Task.WhenAll(new Task[] { t0, t1, t2, t3, t4 }); 

if (tr.IsCompleted) 
    return false; 
else 
    return tcs.Task.Result; 

但它不工作:(

+0

难道你不想使用WaitAny而不是WaitAll吗?请参阅http://msdn.microsoft.com/en-us/library/dd270672.aspx – dash 2012-08-06 08:26:56

回答

4

一个办法是创造你想要的(识别的结果,如果这是必需的)任何类型的TaskCompletionSource。然后添加一个延续到每个任务,c如果结果为真,则分配TaskCompletionSource.TrySetResult。然后等待TaskCompletionSource.Task。它避免你不必反复调用Task.WaitAny,检查结果等

所有任务已经返回false ...在.NET 4.5,这将是相当容易的,由经Task.WhenAll创建另一个任务棘手位被察觉 - 那么你只需等待{ success, all failed }的第一个完成即可。

+0

当任务返回true时它工作正常。但我试图处理Task.WhenAll,没有任何东西似乎被注意到...... – Florian 2012-08-06 12:04:02

+0

@Florian:那么你使用.NET 4.5吗?值得更新这个问题或为'WhenAll'案件开始一个新的问题。从根本上说,你是在“第一个{任何返回true,所有都返回}”之后发生的,对吧? – 2012-08-06 12:33:05

+0

是的,我正在使用.Net 4.5。我想我滥用。当所有方法。我会更新这个问题 – Florian 2012-08-06 12:39:29

3

您需要WaitHandle.WaitAny在开始的时候,你建立一个WaitHandle[],每一个等待一个Task,当成功地执行任务。 (并获得true结果如你预期),则信号对应的WaitHandle

+0

是的 - 我猜他实际上想要'WaitAny',但不清楚他想要做什么。在第一个问题中,他只是对所有人的答案进行了异议。在第二次迭代中,他在没有任何异或的'TrySetResult'中设置单个结果。所以我猜都会回答'WhenAll'和'WhenAny'很好 – Jasper 2012-08-07 09:23:12

0

您可以使用ManualResetEvent,这几乎会使您的isFound属性冗余,例如

private static ManualResetEvent found = new ManualResetEvent(false); 
... 

Task.Factory.StartNew<bool>(() => Find(paramA, paramB));  
Task.Factory.StartNew<bool>(() => Find(paramC, paramD));  
Task.Factory.StartNew<bool>(() => Find(paramE, paramF));  
Task.Factory.StartNew<bool>(() => Find(paramG, paramH)); 

var result = found.WaitOne(TimeSpan.FromSeconds(10)); // wait with timeout of 10 secs 

// do something with result 

... 
private static bool Find(int[,] m1, int[,] m2)   
{   
    if (found.WaitOne(0)) // check whether MSE has already been set 
     return false;   

    // Do work... 
    found.Set(); 
}