Aggregate and forward exceptions from multiple threads - c#

In my method I start multiple threads and then wait until they finish their work (something like fork-join pattern).
using (var countdownEvent = new CountdownEvent(runningThreadsCount))
{
for (int i = 0; i < threadsCount; i++)
{
var thread = new Thread(new ThreadStart(delegate
{
// Do something
countdownEvent.Signal();
}));
thread.Start();
}
countdownEvent.Wait();
}
Now I need to be able to catch exception in this threads (lets assume that // Do something may throw an exception), pass exception to the main thread, unblock it (since it is waiting on countdownEvent) and re-throw the exception.
What is the most elegant way to achieve that?

Solved my problem with Tasks API. Thanks flq for suggestion!
var cancellationTokenSource = new CancellationTokenSource();
var tasks = new Task[threadsCount]
for (int i = 0; i < threadsCount; i++)
{
tasks[i] = Task.Factory.StartNew(
delegate
{
// Do something
}, cancellationTokenSource.Token);
}
try
{
Task.WaitAll(tasks);
}
catch (AggregateException ae)
{
cancellationTokenSource.Cancel();
throw ae.InnerExceptions[0];
}

Related

C# - How to check if Multi-threading execution has finished? [duplicate]

I have a windows forms app that I am checking all the serial ports to see if a particular device is connected.
This is how I spin off each thread. The below code is already spun off the main gui thread.
foreach (cpsComms.cpsSerial ser in availPorts)
{
Thread t = new Thread(new ParameterizedThreadStart(lookForValidDev));
t.Start((object)ser);//start thread and pass it the port
}
I want the next line of code to wait until all the threads have finished.
I've tried using a t.join in there, but that just processes them linearly.
List<Thread> threads = new List<Thread>();
foreach (cpsComms.cpsSerial ser in availPorts)
{
Thread t = new Thread(new ParameterizedThreadStart(lookForValidDev));
t.Start((object)ser);//start thread and pass it the port
threads.Add(t);
}
foreach(var thread in threads)
{
thread.Join();
}
Edit
I was looking back at this, and I like the following better
availPorts.Select(ser =>
{
Thread thread = new Thread(lookForValidDev);
thread.Start(ser);
return thread;
}).ToList().ForEach(t => t.Join());
Use the AutoResetEvent and ManualResetEvent Classes:
private ManualResetEvent manual = new ManualResetEvent(false);
void Main(string[] args)
{
AutoResetEvent[] autos = new AutoResetEvent[availPorts.Count];
manual.Set();
for (int i = 0; i < availPorts.Count - 1; i++)
{
AutoResetEvent Auto = new AutoResetEvent(false);
autos[i] = Auto;
Thread t = new Thread(() => lookForValidDev(Auto, (object)availPorts[i]));
t.Start();//start thread and pass it the port
}
WaitHandle.WaitAll(autos);
manual.Reset();
}
void lookForValidDev(AutoResetEvent auto, object obj)
{
try
{
manual.WaitOne();
// do something with obj
}
catch (Exception)
{
}
finally
{
auto.Set();
}
}
The simplest and safest way to do this is to use a CountdownEvent. See Albahari.
Store the Thread results in a list after they were spawned and iterate the list - during iteration call join then. You still join linearly, but it should do what you want.
You can use a CountDownLatch:
public class CountDownLatch
{
private int m_remain;
private EventWaitHandle m_event;
public CountDownLatch(int count)
{
Reset(count);
}
public void Reset(int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException();
m_remain = count;
m_event = new ManualResetEvent(false);
if (m_remain == 0)
{
m_event.Set();
}
}
public void Signal()
{
// The last thread to signal also sets the event.
if (Interlocked.Decrement(ref m_remain) == 0)
m_event.Set();
}
public void Wait()
{
m_event.WaitOne();
}
}
Example how to use it:
void StartThreads
{
CountDownLatch latch = new CountDownLatch(availPorts.Count);
foreach (cpsComms.cpsSerial ser in availPorts)
{
Thread t = new Thread(new ParameterizedThreadStart(lookForValidDev));
//start thread and pass it the port and the latch
t.Start((object)new Pair(ser, latch));
}
DoSomeWork();
// wait for all the threads to signal
latch.Wait();
DoSomeMoreWork();
}
// In each thread
void NameOfRunMethod
{
while(running)
{
// do work
}
// Signal that the thread is done running
latch.Signal();
}

How to Catch Exceptions for an array of Tasks

I would like to understand how what I should be using inside of my "try" block when trying to await an Array of Tasks.
I want all tasks to be awaited, regardless if one of them threw an Exception, so that they can all complete.
Should I use:
var tasks = new Task<CasApiRouterModelExtendedInfo>[mbis.Length];
for (int i = 0; i < mbis.Length; i++)
{
tasks[i] = CAS.Service.GetAllRouterInterfacesAsync(mbis[i], false, 2);
}
try
{
Task.WaitAll(tasks);
}
catch (AggregateException ex)
{
Trace.TraceError("Some interface discoveries failed: ");
foreach (var innerEx in ex.InnerExceptions)
{
Trace.TraceError(innerEx.Message);
}
}
foreach (var task in tasks)
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
returnResults.Add(task.Result);
}
}
OR
var tasks = new Task<CasApiRouterModelExtendedInfo>[mbis.Length];
for (int i = 0; i < mbis.Length; i++)
{
tasks[i] = Task.Run(() => CAS.Service.GetAllRouterInterfacesAsync(mbis[i], true, 2));
}
try
{
for (int i = 0; i < tasks.Length; i++)
{
tasks[i].Wait();
}
}
catch (AggregateException ex)
{
Trace.TraceError("Some interface discoveries failed: ");
foreach (var innerEx in ex.InnerExceptions)
{
Trace.TraceError(innerEx.Message);
}
}
foreach (var task in tasks)
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
returnResults.Add(task.Result);
}
}
Also, does that "task.Status == TaskStatus.RanToCompletion" return true as long as the task didn't throw an Exception (is this a good way to do this check)?
Is there any reason you would prefer B? I can't think of one.
B is incorrect in case of error.
Also, it is bad style. Your intention is to wait for all tasks, so say that in the code. No need for manual loops obscuring what you want to accomplish.
Also, does that "task.Status == TaskStatus.RanToCompletion" return true as long as the task didn't throw an Exception (is this a good way to do this check)?
This checks for successful completion. Likely, this is what you want. Tasks can end up cancelled but likely you will consider that as an error case.
task.Result != null
Is that really what you want? task.Result is never set to null by the system. This can only happen if GetAllRouterInterfacesAsync makes the result null.

detect when each task is complete

I want to update a progressbar as each task is completed below.
The method var continuation2 = Task.Factory.ContinueWhenAny(..... doesnt work.
What is the correct way to do this?
C# Code
private void radButtonInsertManyErrors_Click(object sender, EventArgs e)
{
try
{
radProgressBarStatus.Maximum = int.Parse(radTextBoxNumberofErrorsInsert.Text);
radProgressBarStatus.Value1 = 0;
Task<int>[] tasks = new Task<int>[int.Parse(radTextBoxNumberofErrorsInsert.Text)];
for (int i = 0; i < int.Parse(radTextBoxNumberofErrorsInsert.Text); i++)
{
int x = i;
tasks[i] = new Task<int>(() =>
{
//insert the error into table FA_Errors
Accessor.Insert_FAErrors(BLLErrorCodes.BLL_Error_Codes.Error_Log_Event_Login.ToString(),
(int)BLLErrorCodes.BLL_Error_Codes.Error_Log_Event_Login,
"Some Error", "",
MethodBase.GetCurrentMethod().DeclaringType.Namespace.ToString(),
MethodBase.GetCurrentMethod().Name.ToString(),
BLLErrorCategory.BLL_Error_Category.WEB_APP.ToString(),
"pc source", "damo",
sConn.ToString());
return 1;
});
}
var continuation = Task.Factory.ContinueWhenAll(
tasks,
(antecedents) =>
{
RadMessageBox.Show("Finished inserting errors ");
});
var continuation2 = Task.Factory.ContinueWhenAny(
tasks,
(antecedents) =>
{
radProgressBarStatus.Value1++;
});
for (int i = 0; i < int.Parse(radTextBoxNumberofErrorsInsert.Text); i++)
tasks[i].Start();
// Use next line if you want to block the main thread until all the tasks are complete
//continuation.Wait();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
You can use this function:
public static void TaskProgress(IEnumerable<Task> tasks, Action<int> callback)
{
int count = 0;
foreach (var task in tasks)
task.ContinueWith(t => callback(Interlocked.Increment(ref count)));
}
It will call the callback each time a task completes with the number of currently completed tasks. Note that the callbacks are not synchronized, so it can be called while the previous callback is still running.
Set up a continuation with each of the tasks. Keep a (thread-safe) counter on how many completed and update the UI on completion of each task.
Actually, Task.WhenAll does keep such a counter under the hood. It is just not accessible.

Executing method by timer inside threadpool

In my multi threaded web app I invoke in the ThreadPool SomeMethod which can throw an exception. Suppose I want to make a few attempts if it causes an exception at first call. I decide to use System.Timers.Timer inside my action for attempts. Can I use the code below? Is it safely?
static void Caller()
{
ThreadPool.QueueUserWorkItem(action =>
{
try
{
SomeMethod();
Console.WriteLine("Done.");
}
catch
{
var t = new System.Timers.Timer(1000);
t.Start();
var count = 0;
t.Elapsed += new System.Timers.ElapsedEventHandler((o, a) =>
{
var timer = o as System.Timers.Timer;
count++;
var done = false;
Exception exception = null;
try
{
Console.WriteLine(count);
SomeMethod();
done = true;
}
catch (Exception ex)
{
exception = ex;
}
if (done || count == 10)
{
Console.WriteLine(String.Format("Stopped. done: {0}, count: {1}", done, count));
t.Stop();
if (!done) throw exception;
}
});
}
});
Thread.Sleep(100000);
}
static void SomeMethod()
{
var x = 1 / new Random().Next(0, 2);
}
You should Dispose each Timer after use, that's for sure. But, probably you could do something even simpler:
static void Main()
{
ThreadPool.QueueUserWorkItem(action =>
{
while (TrySomeMethod() == false)
Thread.Sleep(1000);
});
// wait here
Console.Read();
}
static bool TrySomeMethod()
{
try
{
SomeMethod();
return true;
}
catch
{
return false;
}
}
I do not think that using a timer in a thread pool thread is a safe approach. I may be wrong, but the timer will raise its elapsed event when the thread method has already been finished to execute. In this case, the exception will be thrown. Also, I do not see that you are not disposing the timer which leads to resource leaks. If you explain why you need the timer, I will try to find a safe solution...
I don't see the point of using a Timer within a ThreadPool queue, because the ThreadPool would spawn a new thread, and the Timer would spawn a new thread as well.
I would just have a loop within that delegate, because it would not block the main thread either way. Groo showed a good example of that.

very unique exception for BlockingCollection on .net 4.0

i'm using the BlockingCollection for a Producer Consumer pattern and i got an excecption i think to write a patent on it- only two results in google!
the expection is "CompleteAdding may not be used concurrently with additions to the collection" and it happens when i TryAdd on th BlockingCollection as Follows:
public void EnqueueTask(T item)
{
if (!_cancellationTokenSource.IsCancellationRequested)
{
_workerQueue.Add(item);
}
}
the CompleteAdding is called on the dispose of the Consumer-Producer wrapper class:
public void Dispose()
{
if (!_IsActive)
return;
_IsActive = false;
_cancellationTokenSource.Cancel();
_workerQueue.CompleteAdding();
// Wait for the consumer's thread to finish.
for (int i = 0; i < _workers.Length; ++i)
{
Task t1 = Task.Factory.StartNew(() =>
{
try
{
if (!_workers[i].Join(4000))
LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
}
catch (Exception ex)
{
OnLogged(ex.Message + ex.StackTrace);
}
});
}
// Release any OS resources.
}
Anyone from microsoft got an idea? should i sleep after the cancelation and before calling the CompleteAdding?
Look at this piece of the code:
for (int i = 0; i < _workers.Length; ++i)
{
Task t1 = Task.Factory.StartNew(() =>
{
try
{
if (!_workers[i].Join(4000)) << == Here
LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
}
In _workers[i].Join(4000), the value of i is not what you think it is. Try again with:
for (int i = 0; i < _workers.Length; ++i)
{
int j = i; // copy
Task t1 = Task.Factory.StartNew(() =>
{
try
{
if (!_workers[j].Join(4000)) // j
LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
}
In your version, the variable 'i' is captured and all Tasks use the same var. All but the first few will see i == _workers.Length because they are executed after the for-loop is completed.
It is a classic lambda + captured var problem.

Categories

Resources