How to make exception be awared of before calling Task.WaitAll? - c#

In my case, I created tasks by:
IList<Task> Tasks = new List<Task>();
Tasks.Add(Task.Run(async () => { await functionAsync();}));
I need these tasks to run infinitely in order to continuously processing some incoming data. However, in cases when some fatal error/exceptions happen, I need to end the program by canceling all the tasks. I put together an simple example to simulate what I intended to do , but it does not work. I thought a task will considered to be end when throw an exception, and the WaitAny should return with AggregatedException, but it doesn't seem to be how it actually works. So, how can I make it correct?
public static void Main(string[] args)
{
Console.WriteLine(nameof(Main));
for (int i = 1; i < 5; i++)
{
var i1 = i;
_tasks.Add(Task.Run(async () => { await Tryme(i1); }));
}
try
{
Task.WaitAny(_tasks.ToArray());
}
catch (Exception e)
{
Console.WriteLine("Stop my program if any of the task in _tasks throw exception");
Console.WriteLine(e);
}
Console.ReadLine();
}
private static async Task Tryme(int i)
{
Console.WriteLine($"I'm {i}");
if (i == 3)
{
Console.WriteLine($"{i} is throwing the exception");
throw new Exception("fake one");
}
await Task.Delay(TimeSpan.MaxValue);
}

Instead of manually canceling the entire task-chain, you can use TPL Dataflow which falts the entire block for you if an unhandeled exception occurs, or can be configured to behave in other modes if needed:
var actionBlock = new ActionBlock<int>(i => TryMe(i));
foreach (var num in Enumerable.Range(0, 100))
{
actionBlock.Post(num);
}
try
{
await actionBlock.Completion();
}
catch (Exception e)
{
// Block finished prematurely, handle exception.
}
Note Dataflow takes care of parralisation for you, no need to manually create tasks.

Got some clue from this post . It looks that WaitAny will not throw any exception. I got the exception by:
int faultIndex = Task.WaitAny(_tasks.ToArray());
if (_tasks[faultIndex].IsFaulted)
{
Console.WriteLine($"{faultIndex} end");
throw _tasks[faultIndex].Exception;
}

Related

Catching exceptions immediately does not work with Task.WhenAll

I have two instances of a class which creates a UDP socket to receive data from UDP clients. If one of the instances throws an exception I want to handle it immediately in a higher layer. In my program they're started with await Task.WhenAll(recv1.StartAsync(), recv2.StartAsync). This however waits for all tasks to finish before the first exception is thrown. Any ideas on how to resolve this problem?
static async Task Main(string[] args)
{
var udpReceiver1 = new UdpReceiver(localEndpoint1);
var udpReceiver2 = new UdpReceiver(localEndpoint2);
var cts = new CancellationTokenSource();
try
{
await Task.WhenAll(udpReceiver1.StartAsync(cts.Token), udpReceiver2.StartAsync(cts.Token));
}
catch (Exception e)
{
// Handle Exception...
cts.Cancel();
}
}
class UdpReceiver
{
public UdpReceiver(IPEndPoint endpoint)
{
udpClient = new UdpClient(endpoint);
}
public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
var result = await ReceiveAsync(cancellationToken);
var message = Encoding.UTF8.GetString(result.Buffer);
Trace.WriteLine($"UdpClient1 received message:{Encoding.UTF8.GetString(result.Buffer)}");
// throw new Exception("UdpClient1 raising exception");
}
}
}
private async Task<UdpReceiveResult> ReceiveAsync(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<UdpReceiveResult>();
using (cancellationToken.Register(() => tcs.TrySetCanceled(), false))
{
var task = udpClient.ReceiveAsync();
var completedTask = await Task.WhenAny(task, tcs.Task);
var result = await completedTask.ConfigureAwait(false);
return result;
}
}
private UdpClient udpClient;
}
Update 1: Task.WhenAny would be a viable solution. Thanks #CamiloTerevinto
try
{
await await Task.WhenAny(udpReceiver1.StartAsync(cts.Token), udpReceiver2.StartAsync(cts.Token));
}
catch (Exception e)
{
// Handle Exception...
cts.Cancel();
}
Update 2: For a more fine grained exception handling of all tasks I'd go with my own adapted implementation of Task.WhenAll proposed by #Servy.
The behavior is different enough from the framework WhenAll implementation that you're probably best just writing your own adapted version, fortunately it's not particularly hard to implement. Just attach a continuation to every single task, if any is cancelled or faulted, the resulting task does the same, if it succeeds store the result, and if the last task was the one to succeed, complete the task with all of the stored results.
public static Task<IEnumerable<TResult>> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks)
{
var listOfTasks = tasks.ToList();
if (listOfTasks.Count == 0)
{
return Task.FromResult(Enumerable.Empty<TResult>());
}
var tcs = new TaskCompletionSource<IEnumerable<TResult>>();
var results = new TResult[listOfTasks.Count];
int completedTasks = 0;
for (int i = 0; i < listOfTasks.Count; i++)
{
int taskIndex = i;
Task<TResult> task = listOfTasks[i];
task.ContinueWith(_ =>
{
if (task.IsCanceled)
tcs.TrySetCanceled();
else if (task.IsFaulted)
tcs.TrySetException(task.Exception.InnerExceptions);
else
{
results[taskIndex] = task.Result;
if (Interlocked.Increment(ref completedTasks) == listOfTasks.Count)
{
tcs.TrySetResult(results);
}
}
});
}
return tcs.Task;
}
As with so many task based generic operations, you also need a version without the results, and if you don't want to deal with a notable overhead, you really need to just copy-paste the result-based approach but with all of the results ripped out, which isn't hard, just inelegant. Turning all of these tasks into tasks with a result would also work, but for an operation like this the overhead is likely problematic.
public static Task WhenAll(IEnumerable<Task> tasks)
{
var listOfTasks = tasks.ToList();
if (listOfTasks.Count == 0)
{
return Task.CompletedTask;
}
var tcs = new TaskCompletionSource<bool>();
int completedTasks = 0;
for (int i = 0; i < listOfTasks.Count; i++)
{
int taskIndex = i;
Task task = listOfTasks[i];
task.ContinueWith(_ =>
{
if (task.IsCanceled)
tcs.TrySetCanceled();
else if (task.IsFaulted)
tcs.TrySetException(task.Exception.InnerExceptions);
else
{
if (Interlocked.Increment(ref completedTasks) == listOfTasks.Count)
{
tcs.TrySetResult(true);
}
}
});
}
return tcs.Task;
}
There's probably a way to do it but I can't think of one without making your code very messy. It'd be better to handle the exception in the actual task. If you need to handle it with common code, use a handler delegate.
static async Task Main(string[] args)
{
var cts = new CancellationTokenSource();
//This is our common error handler
void HandleException(Exception ex)
{
Log("Exception!" + ex.Message);
cts.Cancel();
}
var udpReceiver1 = new UdpReceiver(localEndpoint1);
var udpReceiver2 = new UdpReceiver(localEndpoint1);
//We pass the handler as one of the arguments
await Task.WhenAll(udpReceiver1.StartAsync(cts.Token, HandleException), udpReceiver2.StartAsync(cts.Token, HandleException));
}
class UdpReceiver
{
public async Task StartAsync(CancellationToken cancellationToken, Action<Exception> errorHandler)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
//Main logic goes here
}
}
catch(Exception ex)
{
errorHandler(ex); //Call common error handling code
}
}
You could await the tasks in two steps. In the first step await any of them to complete, and in case of failure initiate the cancellation. Don't handle the exception in this step. Delay the exception handling until the second step, after awaiting all of the tasks to complete. Both tasks may have failed, so you may want to handle the exception of each one separately.
Task task1 = udpReceiver1.StartAsync(cts.Token);
Task task2 = udpReceiver2.StartAsync(cts.Token);
// Await any task to complete
Task firstCompletedTask = await Task.WhenAny(task1, task2);
if (firstCompletedTask.IsFaulted) cts.Cancel();
try
{
// Await them all to complete
await Task.WhenAll(task1, task2);
}
catch
{
if (task1.IsFaulted) HandleException(task1.Exception.InnerException);
if (task2.IsFaulted) HandleException(task2.Exception.InnerException);
}

Async Task exception not being caught in Try-Catch

I am trying to cancel a Socket.ReceiveAsync() call by calling Socket.Close() and handling the inevitable SocketException.
My problem is that the exception never seems to make it to the try/catch in the calling function. Like this:
public async Task DoSomethingAsync()
{
Try
{
while(true)
{
await DoSomethingMoreSpecificAsync();
....
}
}
catch(Exception ex)
{
do some logging... <-- exception never makes it here
}
finally
{
cleanup stuff(); <-- or here...
}
}
public async Task DoSomethingMoreSpecificAsync()
{
var read = await _sock.ReceiveAsync(...); <-- Exception is thrown here
}
So when the exception is thrown, the application never makes it to the catch or finally blocks. It just complains of an unhandled exception. AFAIK async Task methods should propagate their exceptions upwards like any other method. I know that this would be normal if I was using async void...
In the console, after the app has crashed, I see this:
terminate called after throwing an instance of 'PAL_SEHException'
Here is the actual code:
private async Task DoReceiveAsync()
{
var writer = _inputPipe.Writer;
Exception ex = null;
try
{
while (true)
{
var mem = writer.GetMemory(TLS_MAX_RECORD_SIZE);
MemoryMarshal.TryGetArray(mem, out ArraySegment<byte> buf);
//Since OpenSSL will only gve us a complete record, we need a func that will piece one back together from the stream
await ReadRecord(mem);
var res = _ssl.Read(buf.Array, TLS_MAX_RECORD_SIZE);
writer.Advance(res);
var flush = await writer.FlushAsync();
if (flush.IsCanceled || flush.IsCompleted)
break;
}
}
catch (Exception e) { ex = e; }
finally { writer.Complete(ex); }
}
private async Task ReadRecord(Memory<byte> mem)
{
MemoryMarshal.TryGetArray(mem, out ArraySegment<byte> buf);
SslError res;
do
{
var sockRead = await _socket.ReceiveAsync(buf, SocketFlags.None);
if (sockRead == 0) break;
_ssl.ReadBio.Write(buf.Array, sockRead);
var sslRead = _ssl.Peek(peekBuf, TLS_MAX_RECORD_SIZE);
res = _ssl.GetError(sslRead);
} while (res != SslError.SSL_ERROR_NONE);
return;
}

How to keep a thread on hold in a method untill another therad Ends in C#

I have 2 Methods, One method call the other. thread is declared inside the 2nd method and that method will return a boolean output. so when i call the 2nd method i cant control the output, and 1st method returns the success message before the thread ends. I want the boolean output after when the thread ends. How can i control this?
1st Method
private void AccessElements()
{
TaxoProcess Taxo = new TaxoProcess();
if (Taxo.AccessEntity())
{
MessageBox.Show("Succesfully Extracted Data", "Extract Application", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
2nd Method,
public bool AccessEntity()
{
try
{
bool Status = true;
Thread MainThread = Thread.CurrentThread;
Thread backgroundThread = new Thread(new ThreadStart(() =>
{
for (int i = 0; i < Entities.Count; i++)
{
Thread.Sleep(100);
Dispatcher.FromThread(MainThread).BeginInvoke(new Action(() =>
{
int PercentageValue = (int)(0.5f + ((100f * i) / Entities.Count));
StaticDataProperties.ProgBar.Value = PercentageValue;
}));
}
}));
backgroundThread.Start();
return Status;
}
catch (Exception ex)
{
ErrorException = ex.Message;
return false;
}
}
To fix your problem you could use Thread.Join and do this, place this logic before return statement..
backgroundThread.Join(); // blocks calling thread.
There's two general directions you can attempt to take:
Dirty approach:
Add a temprary boole that is true while the "inner" thread is still running and handle the logic that should be handled after this after a "while"-statement on that boole.
(the while-statement would keep the code looping/"paused" untill the boole's value is falsey)
Neat approach:
Using async tasks and a callback function.
Instead of using threads which use too much memory you can use Tasks and async/await like Charles Mager suggested
private async void AccessElements()
{
TaxoProcess Taxo = new TaxoProcess();
if (await Taxo.AccessEntity())
{
MessageBox.Show("Succesfully Extracted Data", "Extract Application", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
public async Task<bool> AccessEntity()
{
return Task.Run(() =>
{
try
{
for (int i = 0; i < Entities.Count; i++)
{
await Task.Delay(100);
int PercentageValue = (int)(0.5f + ((100f * i) / Entities.Count));
StaticDataProperties.ProgBar.Value = PercentageValue;
}
return true;
}
catch (Exception ex)
{
ErrorException = ex.Message;
return false;
}
});
}
This is shorter and does all the thread managements in the background

Task Status Value is Faulted instead Cancelled

I need to call a method in action of my Task object. My task performs some kind of reading and I cancel the operation if it takes more than 2 seconds to complete.
I have this code as simulation:
var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(2000));
var task = Task.Run(() =>
{
try
{
int i = 0;
while (true)
{
Thread.Sleep(500);
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine("i = {0}", i);
i++;
if (i > 3) throw new InvalidOperationException();
}
}
catch (Exception e)
{
Console.WriteLine("Exception {0}", e.Message);
throw;
}
});
task.ContinueWith(t => Console.WriteLine(t.Status), TaskContinuationOptions.NotOnRanToCompletion);
My console outout is as follows:
This is what I expect and works for me. If I copy the code inside the task and create a method I no longer get the task status as Cancelled. I get status as Faulted. I must know if the operation was cancelled or an exception happened while reading process. I cannot figure out why I do not get the task status as Cancelled here.
var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(2000));
var task = Task.Run(() =>
{
try
{
Tester(cts);
}
catch (Exception e)
{
Console.WriteLine("Exception {0}", e.Message);
throw;
}
});
private static void Tester(CancellationTokenSource cts)
{
int i = 0;
while (true)
{
Thread.Sleep(500);
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine("i = {0}", i);
i++;
if (i > 3) throw new InvalidOperationException();
}
}
You get the expected Canceled result, if you pass the token to Task.Run:
var task = Task.Run(() =>
{
try
{
Tester(cts);
}
catch (Exception e)
{
Console.WriteLine("Exception {0}", e.Message);
throw;
}
}, cts.Token);
The task needs to know which token will throw the OperationCanceledException - only an exception from the correct source cancels the task, every other exception will just fault it.
From MSDN:
When a task instance observes an OperationCanceledException thrown by user code, it compares the exception's token to its associated token (the one that was passed to the API that created the Task). If they are the same and the token's IsCancellationRequested property returns true, the task interprets this as acknowledging cancellation and transitions to the Canceled state.
I don't fully understand why you get the Canceled result in the first place, it looks like it has to do with when the CancellationTokenSource is captured.

Looking for an elegant way to get around "Cannot await in the body of a finally clause"

I have the following function:
private async Task DoSomething(NamespaceConnectionInfo nci)
{
var session = await m_sessionProvider.GetSessionAsync(nci);
SomeLegacySynchronousCode(session);
await m_sessionProvider.EndSessionAsync(session);
}
where EndSessionAsync logs and swallows any exception (like a good destructor).
The problem is that SomeLegacySynchronousCode may throw an exception and then the session leaks.
It is clear to me perfectly why the following code is illegal:
private async Task DoSomething(NamespaceConnectionInfo nci)
{
var session = await m_sessionProvider.GetSessionAsync(nci);
try
{
SomeLegacySynchronousCode(session);
}
finally
{
await m_sessionProvider.EndSessionAsync(session);
}
}
So, I am looking for an alternative that would be both correct and elegant.
Variant I
private async Task DoSomething(NamespaceConnectionInfo nci)
{
var session = await m_sessionProvider.GetSessionAsync(nci);
Exception exc = null;
try
{
SomeLegacySynchronousCode(session);
}
catch (Exception e)
{
exc = e;
}
await m_sessionProvider.EndSessionAsync(session);
if (exc != null)
{
// Wrap to preserve the original stack trace.
throw new AggregateException(exc);
}
}
Variant II
private Task DoSomething(NamespaceConnectionInfo nci)
{
return m_sessionProvider.GetSessionAsync(nci).ContinueWith(t =>
{
Task result = null;
try
{
SomeLegacySynchronousCode(t.Result);
}
finally
{
if (t.Exception == null)
{
result = m_sessionProvider.EndSessionAsync(t.Result);
}
}
return result;
}).Unwrap();
}
Neither are as elegant as the aforementioned illegal async/await version.
I am looking to improve over the two variants that I have proposed, because both are ugly, frankly.
Any ideas?
The commonly-accepted answer appears to be similar to your Variation 1:
You can move the logic outside of the catch block and rethrow the
exception after, if needed, by using ExceptionDispatchInfo.
static async Task f()
{
ExceptionDispatchInfo capturedException = null;
try
{
await TaskThatFails();
}
catch (MyException ex)
{
capturedException = ExceptionDispatchInfo.Capture(ex);
}
if (capturedException != null)
{
await ExceptionHandler();
capturedException.Throw();
}
}
This way, when the caller inspects the exception's StackTrace
property, it still records where inside TaskThatFails it was thrown.

Categories

Resources