I have some tasks which return a bool. I just want wait until when any task returns True firstly. is it possible ?
My first idea was to use a CancellationTokenSource but it was not a good idea because of it throws an exception when I call the Task.WaitAll method.
The second option is to use a bool I pass in reference and if it's true, return directly. it works but it's not performant :
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;
and in the method :
private static bool Find(int[,] m1, int[,] m2, ref bool isFound)
{
if (isFound)
return false;
// Do work...
}
EDIT :
As the answer preconized, I use a TaskCompletionSource<bool>
now :
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;
In this case, when all tasks return false, nothing is noticed and it's normal. However I don't know how to use the WhenAll
method. I tried to add this :
tcs.Task.Wait();
Task tr = Task.WhenAll(new Task[] { t0, t1, t2, t3, t4 });
if (tr.IsCompleted)
return false;
else
return tcs.Task.Result;
But it does not work :(
You could use a ManualResetEvent which would pretty much make your
isFound
property redundant e.g.One option would be to create a
TaskCompletionSource
of whatever type you want (to identify the result, if that's required). Then add a continuation to each of the tasks, callingTaskCompletionSource.TrySetResult
if the result is true.Then just wait on the
TaskCompletionSource.Task
. It avoids you having to callTask.WaitAny
repeatedly, checking for results etc.The tricky bit is noticing when all tasks have returned false... in .NET 4.5 this would be reasonably easy, by creating another task via
Task.WhenAll
- then you'd just wait for the first of{ success, all failed }
to complete.You need
WaitHandle.WaitAny
. At the very beginning you set up aWaitHandle[]
, each one waits aTask
, when the task is successfully executed (and gettrue
as a result as you expected), you signal the correspondingWaitHandle
.