Task.Factory.ContinueWhenAny continue when any task finish without exception - c#

I have 3 tasks in my application that are responsible for getting data from databases.
Till now I had all tasks executed one after one. If first finished and had Result then this was my data, if now I started second task and I checked again.
Recently I found information that I can start multiple tasks and continue when one of them finish using Task.Factory.ContinueWhenAny. This works fine if all my task don't throw any Exceptions, but if any Task fails I can't get results I want.
For example:
var t1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
return 1;
});
var t2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(2000);
throw new Exception("My error");
return 2;
});
var t3 = Task.Factory.StartNew(() =>
{
Thread.Sleep(4000);
return 3;
});
Task.Factory.ContinueWhenAny(new[] {t1, t2,t3}, (t) =>
{
Console.WriteLine(t.Result);
});
This code starts 3 tasks and waits till one of them finish.
Because t2 throws exception after 2 seconds it is the one that is available in ContinueWhenAny.
From code above I would like to get 3 in t.Result.
Is there an option to continue only when task finished successfully? Something like Task.Factory.ContinueWhenAnyButSkipFailedTasks
EDIT 1
This is my solution for now based on #Nitram answer:
var t1 = Task.Factory.StartNew(() =>
{
var rnd = new Random();
Thread.Sleep(rnd.Next(5,15)*1000);
throw new Exception("My error");
return 1;
});
var t2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(2000);
throw new Exception("My error");
return 2;
});
var t3 = Task.Factory.StartNew(() =>
{
throw new Exception("My error");
return 3;
});
var tasks = new List<Task<int>> { t1, t2, t3 };
Action<Task<int>> handler = null;
handler = t =>
{
if (t.IsFaulted)
{
tasks.Remove(t);
if (tasks.Count == 0)
{
throw new Exception("No data at all!");
}
Task.Factory.ContinueWhenAny(tasks.ToArray(), handler);
}
else
{
Console.WriteLine(t.Result);
}
};
Task.Factory.ContinueWhenAny(tasks.ToArray(), handler);
What I need now is how to throw exception when all tasks throw exception?
Maybe this can be changed into single method that would return task - something like child tasks?

There is a overload to the ContinueWhenAny function that does what you want.
Simply set the TaskContinuationOptions to OnlyOnRanToCompletion and the failed tasks will be ignored.
Task.Factory.ContinueWhenAny(new[] {t1, t2,t3}, (t) =>
{
Console.WriteLine(t.Result);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
So we concluded that this answer is actually wrong.
The removal of the tasks from a list seems to be the only way I can think of.
I tried to put this into some lines of code. Here you go:
var tasks = new List<Task> {t1, t2, t3};
Action<Task> handler = null;
handler = (Task t) =>
{
if (t.IsFauled) {
tasks.Remove(t);
Task.Factory.ContinueWhenAny(tasks.ToArray, handler);
} else {
Console.WriteLine(t.Result);
}
};
Task.Factory.ContinueWhenAny(tasks.ToArray, handler);
I am not very firm with C#, but I hope it gives you a idea. What is basically happening is that every time a task that is faulted is handled, this task is removed from the list of known tasks and the function waits for the next one.
Okay and now the entire thing with .NET 4.5 and the async-await pattern.
await basically gives you the ability to register what ever is written after the await into a continuation.
So this is nearly the same pattern with async-await.
var tasks = new List<Task> {t1, t2, t3};
while (tasks.Any())
{
var finishedTask = await Task.WhenAny(tasks);
if (finishedTask.IsFaulted)
{
tasks.Remove(finishedTask);
}
else
{
var result = await finishedTask;
Console.WriteLine(result);
return;
}
}
The only difference is that the outer function needs to be a async function for that one. This means upon encountering the first await the outer function will return the Task that holds the continuation.
You can add a surrounding function that blocks until this function is done. The async-await pattern gives you the ability to write asynchronous non-blocking code that "looks" like simply synchronouse code.
Also I suggest you use the Task.Run function to spawn your tasks instead of the TaskFactory. It will spare from some problems later on. ;-)

If you're using .NET 4.5, you can use Task.WhenAny to easily achieve what you want:
public async Task<int> GetFirstCompletedTaskAsync()
{
var tasks = new List<Task>
{
Task.Run(() =>
{
Thread.Sleep(5000);
return 1;
}),
Task.Run(() =>
{
Thread.Sleep(2000);
throw new Exception("My error");
}),
Task.Run(() =>
{
Thread.Sleep(4000);
return 3;
}),
};
while (tasks.Count > 0)
{
var finishedTask = await Task.WhenAny(tasks);
if (finishedTask.Status == TaskStatus.RanToCompletion)
{
return finishedTask
}
tasks.Remove(finishedTask);
}
throw new WhateverException("No completed tasks");
}

What if just simply do this (at least it worked for me):
bool taskFinishedFlag = false;
Task t1 = Task.Factory.StartNew(() => { Thread.Sleep(4000); return 1; });
Task t2 = Task.Factory.StartNew(() => { Thread.Sleep(2000);
throw new Exception("");return 2; });
Task t3 = Task.Factory.StartNew(() => { Thread.Sleep(4000); return 3; });
Task<int>[] Tasks = new[] { t1, t2, t3 };
for (int i = 0; i < Tasks.Length; i++)
{
Tasks[i].ContinueWith((t) =>
{
if (taskFinishedFlag) return;
taskFinishedFlag = true;
Console.WriteLine(t.Result);
}, TaskContinuationOptions.NotOnFaulted);
}

Related

Is there default way to get first task that finished successfully?

Lets say that i have a couple of tasks:
void Sample(IEnumerable<int> someInts)
{
var taskList = someInts.Select(x => DownloadSomeString(x));
}
async Task<string> DownloadSomeString(int x) {...}
I want to to get the result of first successful task. So, the basic solution is to write something like:
var taskList = someInts.Select(x => DownloadSomeString(x));
string content = string.Empty;
Task<string> firstOne = null;
while (string.IsNullOrWhiteSpace(content)){
try
{
firstOne = await Task.WhenAny(taskList);
if (firstOne.Status != TaskStatus.RanToCompletion)
{
taskList = taskList.Where(x => x != firstOne);
continue;
}
content = await firstOne;
}
catch(...){taskList = taskList.Where(x => x != firstOne);}
}
But this solution seems to run N+(N-1)+..+K tasks. Where N is someInts.Count and K is position of first successful task in tasks, so as it's rerunning all task except one that is captured by WhenAny.
So, is there any way to get first task that finished successfully with running maximum of N tasks? (if successful task will be the last one)
All you need to do is create a TaskCompletionSource, add a continuation to each of your tasks, and set it when the first one finished successfully:
public static Task<T> FirstSuccessfulTask<T>(IEnumerable<Task<T>> tasks)
{
var taskList = tasks.ToList();
var tcs = new TaskCompletionSource<T>();
int remainingTasks = taskList.Count;
foreach (var task in taskList)
{
task.ContinueWith(t =>
{
if (task.Status == TaskStatus.RanToCompletion)
tcs.TrySetResult(t.Result);
else
if (Interlocked.Decrement(ref remainingTasks) == 0)
tcs.SetException(new AggregateException(tasks.SelectMany(t1 => t1.Exception.InnerExceptions)));
});
}
return tcs.Task;
}
And a version for tasks without a result:
public static Task FirstSuccessfulTask(IEnumerable<Task> tasks)
{
var taskList = tasks.ToList();
var tcs = new TaskCompletionSource<bool>();
int remainingTasks = taskList.Count;
foreach (var task in taskList)
{
task.ContinueWith(t =>
{
if (task.Status == TaskStatus.RanToCompletion)
tcs.TrySetResult(true);
else
if (Interlocked.Decrement(ref remainingTasks) == 0)
tcs.SetException(new AggregateException(
tasks.SelectMany(t1 => t1.Exception.InnerExceptions)));
});
}
return tcs.Task;
}
The problem with "the first successful task" is what to do if all tasks fail? It's a really bad idea to have a task that never completes.
I assume you'd want to propagate the last task's exception if they all fail. With that in mind, I would say something like this would be appropriate:
async Task<Task<T>> FirstSuccessfulTask(IEnumerable<Task<T>> tasks)
{
Task<T>[] ordered = tasks.OrderByCompletion();
for (int i = 0; i != ordered.Length; ++i)
{
var task = ordered[i];
try
{
await task.ConfigureAwait(false);
return task;
}
catch
{
if (i == ordered.Length - 1)
return task;
continue;
}
}
return null; // Never reached
}
This solution builds on the OrderByCompletion extension method that is part of my AsyncEx library; alternative implementations also exist by Jon Skeet and Stephen Toub.
As a straight forward solution is to wait for any task, check if it is in RanToCompletion state and if not, wait again for any task except the already finished one.
async Task<TResult> WaitForFirstCompleted<TResult>( IEnumerable<Task<TResult>> tasks )
{
var taskList = new List<Task<TResult>>( tasks );
while ( taskList.Count > 0 )
{
Task<TResult> firstCompleted = await Task.WhenAny( taskList ).ConfigureAwait(false);
if ( firstCompleted.Status == TaskStatus.RanToCompletion )
{
return firstCompleted.Result;
}
taskList.Remove( firstCompleted );
}
throw new InvalidOperationException( "No task completed successful" );
}

TaskFactory, Starting a new Task when one ends

I have found many methods of using the TaskFactory but I could not find anything about starting more tasks and watching when one ends and starting another one.
I always want to have 10 tasks working.
I want something like this
int nTotalTasks=10;
int nCurrentTask=0;
Task<bool>[] tasks=new Task<bool>[nThreadsNum];
for (int i=0; i<1000; i++)
{
string param1="test";
string param2="test";
if (nCurrentTask<10) // if there are less than 10 tasks then start another one
tasks[nCurrentThread++] = Task.Factory.StartNew<bool>(() =>
{
MyClass cls = new MyClass();
bool bRet = cls.Method1(param1, param2, i); // takes up to 2 minutes to finish
return bRet;
});
// How can I stop the for loop until a new task is finished and start a new one?
}
Check out the Task.WaitAny method:
Waits for any of the provided Task objects to complete execution.
Example from the documentation:
var t1 = Task.Factory.StartNew(() => DoOperation1());
var t2 = Task.Factory.StartNew(() => DoOperation2());
Task.WaitAny(t1, t2)
I would use a combination of Microsoft's Reactive Framework (NuGet "Rx-Main") and TPL for this. It becomes very simple.
Here's the code:
int nTotalTasks=10;
string param1="test";
string param2="test";
IDisposable subscription =
Observable
.Range(0, 1000)
.Select(i => Observable.FromAsync(() => Task.Factory.StartNew<bool>(() =>
{
MyClass cls = new MyClass();
bool bRet = cls.Method1(param1, param2, i); // takes up to 2 minutes to finish
return bRet;
})))
.Merge(nTotalTasks)
.ToArray()
.Subscribe((bool[] results) =>
{
/* Do something with the results. */
});
The key part here is the .Merge(nTotalTasks) which limits the number of concurrent tasks.
If you need to stop the processing part way thru just call subscription.Dispose() and everything gets cleaned up for you.
If you want to process each result as they are produced you can change the code from the .Merge(...) like this:
.Merge(nTotalTasks)
.Subscribe((bool result) =>
{
/* Do something with each result. */
});
This should be all you need, not complete, but all you need to do is wait on the first to complete and then run the second.
Task.WaitAny(task to wait on);
Task.Factory.StartNew()
Have you seen the BlockingCollection class? It allows you to have multiple threads running in parallel and you can wait from results from one task to execute another. See more information here.
The answer depends on whether the tasks to be scheduled are CPU or I/O bound.
For CPU-intensive work I would use Parallel.For() API setting the number of thread/tasks through MaxDegreeOfParallelism property of ParallelOptions
For I/O bound work the number of concurrently executing tasks can be significantly larger than the number of available CPUs, so the strategy is to rely on async methods as much as possible, which reduces the total number of threads waiting for completion.
How can I stop the for loop until a new task is finished and start a
new one?
The loop can be throttled by using await:
static void Main(string[] args)
{
var task = DoWorkAsync();
task.Wait();
// handle results
// task.Result;
Console.WriteLine("Done.");
}
async static Task<bool> DoWorkAsync()
{
const int NUMBER_OF_SLOTS = 10;
string param1="test";
string param2="test";
var results = new bool[NUMBER_OF_SLOTS];
AsyncWorkScheduler ws = new AsyncWorkScheduler(NUMBER_OF_SLOTS);
for (int i = 0; i < 1000; ++i)
{
await ws.ScheduleAsync((slotNumber) => DoWorkAsync(i, slotNumber, param1, param2, results));
}
ws.Complete();
await ws.Completion;
}
async static Task DoWorkAsync(int index, int slotNumber, string param1, string param2, bool[] results)
{
results[slotNumber] = results[slotNumber} && await Task.Factory.StartNew<bool>(() =>
{
MyClass cls = new MyClass();
bool bRet = cls.Method1(param1, param2, i); // takes up to 2 minutes to finish
return bRet;
}));
}
A helper class AsyncWorkScheduler uses TPL.DataFlow components as well as Task.WhenAll():
class AsyncWorkScheduler
{
public AsyncWorkScheduler(int numberOfSlots)
{
m_slots = new Task[numberOfSlots];
m_availableSlots = new BufferBlock<int>();
m_errors = new List<Exception>();
m_tcs = new TaskCompletionSource<bool>();
m_completionPending = 0;
// Initial state: all slots are available
for(int i = 0; i < m_slots.Length; ++i)
{
m_slots[i] = Task.FromResult(false);
m_availableSlots.Post(i);
}
}
public async Task ScheduleAsync(Func<int, Task> action)
{
if (Volatile.Read(ref m_completionPending) != 0)
{
throw new InvalidOperationException("Unable to schedule new items.");
}
// Acquire a slot
int slotNumber = await m_availableSlots.ReceiveAsync().ConfigureAwait(false);
// Schedule a new task for a given slot
var task = action(slotNumber);
// Store a continuation on the task to handle completion events
m_slots[slotNumber] = task.ContinueWith(t => HandleCompletedTask(t, slotNumber), TaskContinuationOptions.ExecuteSynchronously);
}
public async void Complete()
{
if (Interlocked.CompareExchange(ref m_completionPending, 1, 0) != 0)
{
return;
}
// Signal the queue's completion
m_availableSlots.Complete();
await Task.WhenAll(m_slots).ConfigureAwait(false);
// Set completion
if (m_errors.Count != 0)
{
m_tcs.TrySetException(m_errors);
}
else
{
m_tcs.TrySetResult(true);
}
}
public Task Completion
{
get
{
return m_tcs.Task;
}
}
void SetFailed(Exception error)
{
lock(m_errors)
{
m_errors.Add(error);
}
}
void HandleCompletedTask(Task task, int slotNumber)
{
if (task.IsFaulted || task.IsCanceled)
{
SetFailed(task.Exception);
return;
}
if (Volatile.Read(ref m_completionPending) == 1)
{
return;
}
// Release a slot
m_availableSlots.Post(slotNumber);
}
int m_completionPending;
List<Exception> m_errors;
BufferBlock<int> m_availableSlots;
TaskCompletionSource<bool> m_tcs;
Task[] m_slots;
}

Wait for all Threads

I have a little problem with Threads in this code..
I just want to run a lot of tasks together, and continue when all of them finish.
while (true)
{
// Run tasks together:
foreach (object T in objectsList)
{
if (T.something>0)
var task = Task.Factory.StartNew(() => T.RunObject());
task.ContinueWith(delegate { ChangeObject(T, 1); }, TaskContinuationOptions.NotOnFaulted);
}
// <-- Here I want to wait for all the task to be finish.
// I know its task.Wait() but how to waitAll()?
System.Threading.Thread.Sleep(this.GetNextTime());
var RefreshObjects = new Task(loadObjectsList); RefreshObjects .Start(); RefreshObjects.Wait();
}
I don't know how many objects will be in objectsList and I don't know if T.something will be > 0.
so I can't just use:
Task[] Tasks = new Task[objectsList.count()]
for (int T=0; T<objectsList.count(); ++T)
{
if (objectsList[T].something>0)
var task = Task.Factory.StartNew(() => objectsList[T].RunObject());
task.ContinueWith(delegate { ChangeObject(objectsList[T], 1); }, ...);
}
Task.WaitAll(Tasks);
Because Tasks will contains nulls when objectsList[T].something!>0...
Thanks for any advice!
Just switch the condition and create a List of tasks only for the objects which matches your criteria.
var tasks = objectsList
.Where(x => x.Something() > 0)
.Select(x => {
var task = Task.Factory.StartNew(() => x.RunObject());
task.ContinueWith(t => ChangeObject(....));
return task;
})
.ToArray();
Task.WaitAll(tasks);
Your code sample just waits for RunObject()to complete! If this is desired skip the rest of my answer. If you want to wait for the continuation to complete, too you can use this
var tasks = objectsList
.Where(x => x.Something() > 0)
.Select(x => Task.Factory.StartNew(() => x.RunObject()).ContinueWith(t => ChangeObject(....)))
.ToArray();
Task.WaitAll(tasks);
because ContinueWith generates a new Task.
If objectsList implements IEnumerable, (as an array does),
(And there are less than 64 objects in the list), you can use this:
public delegate void SyncDelegatesInParallelDelegate<in T>(T item);
public static class ParallelLinqExtensions
{
public static void SyncDelegatesInParallel<T>(
this IEnumerable<T> list,
SyncDelegatesInParallelDelegate<T> action)
{
var foundCriticalException = false;
Exception exception = null;
var waitHndls = new List<WaitHandle>();
foreach (var item in list)
{
// Temp copy of session for modified closure
var localItem = item;
var txEvnt = new ManualResetEvent(false);
// Temp copy of session for closure
ThreadPool.QueueUserWorkItem(
depTx =>
{
try { if (!foundCriticalException) action(localItem); }
catch (Exception gX)
{ exception = gX; foundCriticalException = true; }
finally { txEvnt.Set(); }
}, null);
waitHndls.Add(txEvnt);
}
if (waitHndls.Count > 0) WaitHandle.WaitAll(waitHndls.ToArray());
if (exception != null) throw exception;
}
}
you would call it like this
objectsList.SyncDelegatesInParallel(delegate { ChangeObject(T, 1);});

How to signal threads to stop in c# in any one has exception

Suppose that there are 5 threads, T1, T2, T3, T4 and T5 and 5 of them are currently running. My requirement is to stop all 4 threads in case any one of the 5 threads has any exceptions. How can this be achieved. I am using c# 4.0
This can be achieved by using CancellationTokens. CancellationTokenSource can be shared between your threads.
Here is an example with Tasks, but you can use Threads\ThreadPool
CancellationTokenSource cts = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
while (1 == 1)
{
Thread.Sleep(1000);
Console.WriteLine("Thread1 in progress...");
if (cts.Token.IsCancellationRequested)
{
Console.WriteLine("Thread1 exiting...");
break;
}
}
}, cts.Token);
Task.Factory.StartNew(() =>
{
while (1 == 1)
{
Thread.Sleep(1000);
Console.WriteLine("Thread2 in progress...");
if (cts.Token.IsCancellationRequested)
{
Console.WriteLine("Thread2 exiting...");
break;
}
}
}, cts.Token);
Task.Factory.StartNew(() =>
{
while (1 == 1)
{
Thread.Sleep(1000);
Console.WriteLine("Thread3 in progress...");
if (cts.Token.IsCancellationRequested)
{
Console.WriteLine("Thread3 exiting...");
break;
}
}
}, cts.Token);
Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
try
{
Console.WriteLine("Thread5 in progress...");
int y = 0;
int x = 1 / y;
}
catch
{
Console.WriteLine("Thread5 requesting cancellation...");
cts.Cancel();
}
}, cts.Token);
and check this link which has quite a few CalcellationTokenSource examples (including shared CancellationCancellationTokenSources):
EDIT:
I should probably mention that instead of "breaking" the loop when calcellation is requested, you could just call ThrowIfCancellationRequested method of the Token. The outcome will be different though - the task will not "run to completion", it will be in "Cancelled" state. You need to consider this when adding task continuations.

Task WaitAny condition

I found some code that uses the Task.WaitAny condition.
Task<int>[] tasks = new Task<int>[3];
tasks[0] = Task.Run(() => { Thread.Sleep(2000); return 1; });
tasks[1] = Task.Run(() => { Thread.Sleep(1000); return 2; });
tasks[2] = Task.Run(() => { Thread.Sleep(3000); return 3; });
while (tasks.Length > 0) {
int i = Task.WaitAny(tasks);
Task<int> completedTask = tasks[i];
Console.WriteLine(completedTask.Result);
var temp = tasks.ToList();
temp.RemoveAt(i);
tasks = temp.ToArray();
}
The code works fine, the result is 2 1 3.
When I tried to change the sleeping intervals
tasks[0] = Task.Run(() => { Thread.Sleep(2000); return 1; });
tasks[1] = Task.Run(() => { Thread.Sleep(1000); return 2; });
tasks[2] = Task.Run(() => { Thread.Sleep(1000); return 3; });
while (tasks.Length > 0) {
int i = Task.WaitAny(tasks);
Task<int> completedTask = tasks[i];
Console.WriteLine(completedTask.Result);
var temp = tasks.ToList();
temp.RemoveAt(i);
tasks = temp.ToArray();
}
I got 1 2 3, despite task number two is the one with the smallest sleeping time and should be the one removed first.
What is happening under the hood?
Thread.Sleep is fairly accurate in pausing the program for the specified number of milliseconds.
Thread.Sleep(1000) means that task will be available for execution after 1000 millisecond.
But which task is to be execute first from all the available tasks is decided by task scheduler,it decide the order of execution of task based on number of threads in thread-pool and many other factors.

Categories