This question already has an answer here:
Exception handling in fire and forget for C# 5 (in .net 4.5)
(1 answer)
Closed 2 years ago.
Let's assume I have a console application with Main method, something like this:
public static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
{
Console.WriteLine("App Unobserved");
};
TaskScheduler.UnobservedTaskException += (sender, eventArgs) =>
{
Console.WriteLine("Task Unobserved");
};
Task.Run(async () => await MyAwesomeMethod());
// other awesome code...
Console.ReadLine();
}
public static async Task MyAwesomeMethod()
{
// some useful work
if (something_went_wrong)
throw new Exception();
// other some useful work
}
So, I just run MyAwesomeMethod (fire-and-forget), and want to do some other job, but I also want to know if there any unhandled exceptions. But application finishes successfully without any sign of problem (exception is just swallowed).
How can I handle exception from MyAwesomeMethod(), without awaiting it or using Task.Run(...).Wait()?
So, I just run MyAwesomeMethod (fire-and-forget)... but I also want to know if there any unhandled exceptions. But application finishes successfully without any sign of problem (exception is just swallowed).
That's not "fire and forget", then. "Fire and forget" literally means that you don't care when (or whether) the task completes (or errors).
How can I handle exception from MyAwesomeMethod(), without awaiting it or using Task.Run(...).Wait()?
Use await anyway:
Task.Run(async () => {
try {
await MyAwesomeMethod();
} catch (Exception ex) {
Console.WriteLine(ex);
}
});
You can check the status of your task once it's done.
Task.Run(() => MyAwesomeMethod()).ContinueWith((task) =>
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
}
else
{
try
{
Logger.LogError(task.Exception.ToString());
Logger.LogMessage("something_went_wrong");
}
catch { }
}
});
You could for example wrap the code in the background task in a try...catch block and raise an event as soon as you enter the catch block (if you do).
Like
event EventHandler<Exception> exceptionInWorker;
and in the task do
try
{
//do something
}
catch (Exception e)
{
exceptionInWorker?.Invoke(this, e);
}
You can subscribe to TaskScheduler.UnobservedTaskException event as you do but with a handler that takes UnobservedTaskExceptionEventArgs as its second parameter, through it you could access the unhandled exception via its Exception property and log all info about it.
Related
i need help with catching exceptions in C#. I have a Windows service which is acting like a wrapper for other modules, so to not have multiple Windows Services i start all modules/agents thats how we call them in that Wrapper Windows Service. Each of this 'agent' is started in a own Task. I am not in control what the agent itself is doing so it can be and will from time to time is such a agent starting also a task or thread and if there an exception is getting thrown, i am not able to catch it. I tried different things but was not able to do so. So if such a exception occurs in production my whole service is crashing and all agents with it, which is a nightmare. I try to simplify it with an example code:
public class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
AppDomain.CurrentDomain.UnhandledException += (o, e) =>
{
Console.WriteLine("CurrentDomain Unhandled Exception: {0}", e.ExceptionObject);
};
TaskScheduler.UnobservedTaskException += (s, e) =>
{
Console.WriteLine("TaskScheduler.UnobservedTaskException Unhandled Exception: {0}", e.Exception);
e.SetObserved();
};
Task.Factory.StartNew(() =>
{
Task.Factory.StartNew(() => throw new Exception("I am a exception ! Catch me !"));
}, TaskCreationOptions.LongRunning)
.ContinueWith((t) =>
t.Exception.InnerExceptions.ToList().ForEach(e => Console.WriteLine("Error executing task.{0}", e)),
TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("If you read this, application is not crashed!");
Console.ReadKey();
Task.Factory.StartNew(() =>
{
throw new Exception("I am a exception ! Catch me !");
});
}
}
So how to catch the exception ? It will not get fetched by any of my handlers.
Important is that i have no influence of that part of code, thats my 'agent' :
Task.Factory.StartNew(() => throw new Exception("I am a exception ! Catch me !"));
Everything else i am able to change.
Edit:
unfortunately provided solution seems to not work for me. I am still not able to catch the exception which occurs. Maybe its getting more clear when i show my original code:
private async Task StartAgent(IAgent agent)
{
_logger.LogInfo("Agent starting with instanceId {0}", agent.GetInstanceGuid());
if (agent == null)
{
throw new ArgumentNullException("agent");
}
try
{
Task task = await Task.Factory.StartNew(async () =>
{
agent.Start();
}, TaskCreationOptions.LongRunning);
await task.ContinueWith((t) =>
{
var aggException = t.Exception.Flatten();
foreach (var exception in aggException.InnerExceptions)
_logger.LogError("Error executing agent task.{0}", exception, t.Id);
}, TaskContinuationOptions.OnlyOnFaulted);
_agents[agent.GetInstanceGuid()].Task = task;
_agents[agent.GetInstanceGuid()].LastTaskStatus = task.Status;
}
catch (Exception e)
{
_logger.LogError("Exception in Agent Task.",e);
}
}
So the agent.Start() is what i am calling in the task everything what happens inside i don't know. The agent can create tasks, threads everything he wants. Start() is also void and i can't change the interface to await it.
[Task.Factory.StartNew] is wrapped in a void method.
Well, then, the code is deliberately ignoring all exceptions. This kind of "fire and forget" is problematic precisely because it ignores the returned task. All exceptions are placed on that task, which is then ignored.
TaskScheduler.UnobservedTaskException catches it
UnobservedTaskException or AppDomain.FirstChanceException are your only real options. Neither of these are particularly nice (i.e., they're global handlers), but they're your only option because the code outside your control is explicitly ignoring exceptions.
Thanks for all the answers. I found a easy solution now which works for me. All i had to do is adding in my 'runtime' part of my app.config this flag
<legacyUnhandledExceptionPolicy enabled="1" />
Like described here: How to prevent an exception in a background thread from terminating an application?
I had the problem even if i was able to catch my exception my service still stops afterwards.
I have created windows service in c# and in that service, I have created 4 threads and running them the background every after 10 sec.
Below is the code:
var ThreadSize = 4;
for (int i = 0; i < ThreadSize; i++)
{
Task.Run(async () =>
{
while (1 == 1)
{
try
{
//Logic
await Task.Delay(10000, cancelSource.Token);
}
catch (Exception ex)
{
//Log the exception
}
}
});
}
The for loop will be executed only once and will create 4 threads. I am using Task.Delay to wait the thread for 10 sec and then again executing my logic. It will go and execute my logic every 10 sec.
The code is working fine, but after some time, my all threads getting terminated (Not working). I mean, the code in the logic is not working after couples of hours.
There is no exception at all.
Can any one suggested what went wrong.
Thanks you in advance.
Edited Code:
CancellationTokenSource cancelSource;
protected override void OnStart(string[] args)
{
cancelSource = new CancellationTokenSource();
Process.StartProcess(cancelSource);
}
protected override void OnStop()
{
cancelSource.Cancel();
}
public static void StartProcess(CancellationTokenSource cancelSource)
{
var ThreadSize = 4;
for (int i = 0; i < ThreadSize; i++)
{
Task.Run(async () =>
{
while (1 == 1)
{
try
{
//Logic
await Task.Delay(10000, cancelSource.Token);
}
catch (Exception ex)
{
//Log the exception
}
}
});
}
}
If any exception occurs within the Task.Run, it will be saved and thrown when the task is awaited. You're not awaiting the task, so any exception that has occurred won't be visible.
You should await the Task.Run by either using the await keyword or call .Wait() on it.
As you're spawning multiple tasks, you could add all of them to a list and then call await Task.WhenAny(tasks) which will return when any of the tasks finishes, so you can act accordingly.
Read this article for more information
Your main problem is in
catch (Exception ex)
{
throw;
}
This effectively means that you don't catch any errors. You might as well remove the try/catch for the same effect, or lack of effect.
The main structure of your Service looks OK, this won't stop by itself. The choice of Task vs Thread is not too important.
Your error happens inside //Logic and is not handled.
You will need some form of logging to find out.
I have a long running operation that I want to cancel after, say 5 secs. Unfortunately, polling for IsCancellationRequested is not possible (long story).
I used the code below to throw an OperationCanceledException inside the cancellation callback. I wanted to catch the exception in the main thread and handle it so that I can exit the application completely.
This doesn't seem to work properly as this results in an unhandled exception and the application doesn't terminate gracefully.
Any help is appreciated. Thanks!
void TestTimeOut()
{
var cts = new CancellationTokenSource();
cts.CancelAfter(5000);
try
{
var task = Task.Run(() => LongRunningOperation(cts.Token));
task.ContinueWith(t => Console.WriteLine("Operation cancelled"), TaskContinuationOptions.OnlyOnFaulted);
task.Wait();
}
catch (AggregateException e)
{
//Handle
}
}
void LongRunningOperation(CancellationToken token)
{
CancellationTokenRegistration registration = token.Register(
() =>
{
throw new OperationCanceledException(token);
});
using (registration)
{
// long running operation here
}
}
Your code has many No-No-es, but I guess you just using it as a demo for your problem.
The Solution is TaskCompletionSource, My Demo is ugly too, too many layers of Task.Run(), if you use Async, you should async all the way down. So don't use it in PRD, study TaskCompletionSource yourself and figure out a better solution.
static void LongRunningOperation(CancellationToken token)
{
TaskCompletionSource<int> tcs1 = new TaskCompletionSource<int>();
token.Register(() => { tcs1.TrySetCanceled(token); });
Task.Run(() =>
{
// long running operation here
Thread.Sleep(10000);
tcs1.TrySetResult(0);
}, token);
tcs1.Task.Wait();
}
You are catching an AggregateException but actuall throwing an OperationCanceledException which will not be caught.
Change to catch all types of exceptions such as
catch (Exception ex) { ... }
to resolve.
Just noticed strange thing: to catch exception in caller from new Task, lambda MUST be marked as async!? Is it really necessary even if delegate has no await operators at all?
try
{
//Task.Run(() => // exception is not caught!
Task.Run(async () => // unnecessary async!?!
{
throw new Exception("Exception in Task");
}).Wait();
}
catch (Exception ex)
{
res = ex.Message;
}
Why there is neccesary for async operator?
All documentation i can find tells that delegate must not return Void and Task must be awaited for exception to propogate up to caller.
Added full code:
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.Run();
}
public void Run()
{
string result;
try
{
result = OnSomeEvent((s, ea) => RunSomeTask());
}
catch (Exception ex) // Try to catch unhandled exceptions here!
{
result = ex.Message;
}
Console.WriteLine(result);
Console.ReadKey();
}
// Some other Framework bult-in event (can not change signature)
public string OnSomeEvent(EventHandler e)
{
e.Invoke(null, new EventArgs());
return "OK";
}
private async Task RunSomeTask()
{
await Task.Run(async () => // do not need async here!!!
//await Task.Run(() => // caller do not catches exceptions (but must)
{
throw new Exception("Exception in Task1");
});
}
}
So the qestion is how to catche ex. without asyn keyword???
Methods that return Task - such as Task.Run or async methods - will place any exceptions on that returned Task. It's up to you to observe that exception somehow. Normally this is done with await, like this:
await Task.Run(() => { throw ... });
In your case, the problem is in this line:
result = OnSomeEvent((s, ea) => RunSomeTask());
In this code, RunSomeTask is returning a Task, and that Task is never awaited. In order to observe the exception, you should await that task.
When using async/await, exceptions are automatically unwrapped at the site of the await. When using a Task and .Wait(), any exception are wrapped when they come out of the Task, and thus getting information requires you to dig into the Task.Exception property, since they do not propagate up the call stack.
See https://dotnetfiddle.net/MmEXsT
I try to catch exceptions from another thread, but can't.
static void Main(string[] args)
{
try
{
Task task = new Task(Work);
task.Start();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine();
}
public static void Work()
{
throw new NotImplementedException();
}
I write try-catch and at method too, but nothing happens.
Please,tell me how to know that exception throw?
Maybe you could show me some example code.
Your code may not raise the exception as the main method will executes too fast and the process will terminate before you got the exception
Here how it would look your code
static void Main(string[] args)
{
Task task = new Task(Work);
task.Start();
var taskErrorHandler = task.ContinueWith(task1 =>
{
var ex = task1.Exception;
Console.WriteLine(ex.InnerException.Message);
}, TaskContinuationOptions.OnlyOnFaulted);
//here you should put the readline in order to avoid the fast execution of your main thread
Console.ReadLine();
}
public static void Work()
{
throw new NotImplementedException();
}
Try to take a look at ContinueWith
The OnlyOnFaulted member of the TaskContinuationOptions enumeration
indicates that the continuation should only be executed if the
antecedent task threw an exception.
task.ContinueWith((Sender) =>
{
////This will be called when error occures
Sender.Result
}, TaskContinuationOptions.OnlyOnFaulted);
Your try/catch wouldn't work. For one reason : because you could very well have gone out of the try block before the exception is thrown, as the Task is done on another thread.
With a Task, there are two ways to get the exceptions.
The first one is to use task.Wait(); in your try block. This method will rethrow any exception thrown by the task.
Then, any exception will be handled on the calling thread in the catch block.
The second one is to use the ContinueWith method. This won't block your calling thread.
task.ContinueWith(t =>
{
// Here is your exception :
DoSomethingWithYour(t.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
Note the following will block the main thread since Wait is employed.
try
{
Task task = Task.Factory.StartNew(Work);
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex.ToString());
}