C# - Get problematic method result by using thread - c#

I have some kind of data processing, that is depnded on the success of problematic method that returns a result that used in my processing.
The external method is problematic since it works slowly, it may crash and throw exceptions of any type, and I don't have its source code.
I want to use thread in the beginning of my processing to save some time, since my processing is long enough even without that problematic method. but there exists a point that I must have a valid result from the problematic method that I cannot continue if it fails.
I want the use the exceptions of the problematic method in the main thread, so they get the same exception handling as any other exceptions that may be thrown by my processing.
Here is my code - it seems ok and it works, but it just looks too cumbersome to me, so this my question: Is there a better approach to manage correctly the call to the problematic method by thread, and its potential exceptions?
My environment is .NET 3.5, so please I would like to get answers focusing that version, but I would like also to learn if there are new approaches for newer .NET versions.
Thank you very much!
public void DoProcess()
{
object locker = new object();
bool problematicCodeFinished = false;
Exception methodException = null;
Result result;
Func<Result> getProblematicResult = new Func<Result>(() => problematicMethod()); //create delegate
//run problematic method it in thread
getProblematicResult.BeginInvoke((ar) => //callback
{
lock(locker)
{
try
{
result = getProblematicResult.EndInvoke();
}
catch (Exception ex)
{
methodException = ex;
}
finally
{
problematicCodeFinished = true;
Monitor.Pulse(locker);
}
}
}, null);
//do more processing in main thread
// ...
// ...
// ...
try
{
//here we must know what was the result of the problematic method
lock (locker)
{
if (!problematicCodeFinished) //wait for problematic method to finish
{
Monitor.Wait(locker);
}
}
//throw problematic method exception in main thread
if (methodException != null)
{
throw methodException;
}
//if we are here we can assume that we have a valid result, continue processing
// ...
// ...
// ...
}
catch (Exception ex) //for the problematic method exception, or any other exception
{
//normal exception handling for my processing
}
}

You're making life hard for yourself. Try using tasks from the TPL instead.
Your code would look like this:
public void DoProcess()
{
var task = Task.Factory.StartNew(() => problematicMethod());
//do more processing in main thread
// ...
// ...
// ...
var result = task.Result;
}

Related

How can I run an async method (that has inside it a try-catch(exception)) from a sync method?

Note this might be a bit different from other questions as I use a try-catch in the async method and I just do nothing in the exception as it is not a critical problem if it was to fail.
I have a method (with more code not shown) coded like this:
public bool ArrangeCardOrder(bool IsFirstToLast)
{
try
{
// Do stuff
}
catch (Exception ex)
{
await Helper.LogCrash();
return false;
}
}
The LogCrash method looks like this:
public static async Task LogCrash(string ex)
{
try
{
var logCrash = new Cosmos.LogCrash()
{
Ex = exception
};
await App.CDB.InsertLogItem(logCrash);
}
catch (Exception)
{
}
}
InsertLogItem is async so I made LogCrash async. But then I have a problem with calling that as ArrangeCardOrder is not async.
Does anyone have a suggestion on how I can call LogCrash from ArrangeCardOrder bearing in mind that I already catch exceptions in LogCrash so I think that's already being handled.
For logging in particular, a common pattern is to always have synchronous log methods that actually just write to an in-memory queue, which is asynchronously written to the actual data store by a separate actor. So the log methods themselves are naturally synchronous (updating an in-memory queue), while the actual log writes (e.g., InsertLogItem) remain asynchronous but not directly called by the logging methods. Since it looks like you're writing your own logging framework (for some reason), I'd recommend this approach.
If you choose not to take the queue approach, then the next-recommended approach is to either go asynchronous all the way or synchronous all the way.
Ideally, your code should be synchronous or asynchronous all the way up. You currently have a method that is asynchronous (e.g., LogCrash). So ideally you should either make all the calling code asynchronous or make LogCrash synchronous.
// Ideal option #1
public async Task<bool> ArrangeCardOrder(bool IsFirstToLast)
{
try
{
// Do stuff
}
catch (Exception ex)
{
await Helper.LogCrash();
return false;
}
}
public static async Task LogCrash(string ex)
{
... // unchanged
}
or the synchronous option:
// Ideal option #2
public bool ArrangeCardOrder(bool IsFirstToLast)
{
try
{
// Do stuff
}
catch (Exception ex)
{
Helper.LogCrash();
return false;
}
}
public static void LogCrash(string ex)
{
try
{
var logCrash = new Cosmos.LogCrash()
{
Ex = exception
};
App.CDB.InsertLogItem(logCrash);
}
catch (Exception)
{
}
}
Note that the synchronous option assumes that InsertLogItem is made synchronous as well.
The next-recommended option is to actually fire-and-forget. Unlike the queue-based approach, fire-and-forget will prevent you from being aware of any logging exceptions. To do fire-and-forget, you can just call the asynchronous method and ignore the task it returns:
public static void LogCrash(string ex)
{
try
{
var logCrash = new Cosmos.LogCrash()
{
Ex = exception
};
var _ = App.CDB.InsertLogItem(logCrash);
}
catch (Exception)
{
}
}
If none of these options are desirable, then you can use a hack to call asynchronous code from synchronous code.
it depends...
If you need the result of Helper.LogCrash() you'll need to make your ArrangeCardOrder() function async too. Consider using ValueTask instead of a normal task, if you typically return a in a non async way for performance reasons.
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1
If you don't need the result and want to stay with the static function design: Simply ignore the result be removing the await keyword
If you plan to replace your Helper with a service approach, you should wait for the result and make ArrangeCardOrder() async too to ensure your Helper service was not disposed.
#Alan2: I think below-mentioned workaround may work for you (either V1 or V2):
public static void LogCrash(string ex)
{
try
{
var logCrash = new Cosmos.LogCrash()
{
Ex = exception
};
// V1: WAIT for RESULT: If you want to wait to get the result
//await App.CDB.InsertLogItem(logCrash);
App.CDB.InsertLogItem(logCrash).ConfigureAwait(false).GetAwaiter().GetResult();
// V2: FIRE AND FORGOT: if you do not want to wait for the result
// var taskInsertLogItem = Task.Run(async() => await App.CDB.InsertLogItem(logCrash););
}
catch (Exception ex)
{
//TODO:
}
}

async/wait: bubbleup exceptions?

This pertains to a program that has to collect some info from a server on the web.
Question: How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
As depicted here, they do not. ErrorHandler never gets hit.
My Main program:
....
try
{
Task.Run(() => CheckAsync());
// bubble to here?
ReadConfiguration();
}
catch (Exception ex)
{
// gather and handle all exceptions here
ErrorHandler.NotifyMe(new[] { "some message" }, ErrorType.Stop); // never gets hit
}
public async Task CheckAsync()
{
await GetServerResponseAsync("slm_check"); // may throw exception
...
if (.....)
throw new Exception("...");
...
}
public async Task GetServerResponseAsync(string command)
{
...
// client = HttpClient()
using (apacheResponse = await client.GetAsync(ServerUrl + "...."))
{
if (....)
throw new Exception("Internal web server error", new Exception("Maybe MySQL server is down"));
using (HttpContent content = apacheResponse.Content)
{
if ( ....)
throw new Exception("error message");
}
}
}
How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
Use await to consume your tasks, instead of ignoring them.
Specifically, this line:
Task.Run(() => CheckAsync());
is getting a Task back from the Task.Run method, which is then ignored. Instead of ignoring that task, the code should be awaiting it:
await Task.Run(() => CheckAsync());
As other commenters have pointed out, the Task.Run here doesn't really make sense. If your operation is asynchronous, it shouldn't need to also run on a background thread. Usually. :) So if you take out the Task.Run, your code would look like:
await CheckAsync();
which will properly propagate the exception.

Task continuous execution even after exception

The method RelatoriosEstaticos.AbrirDataAtual that is within the Task below is returning an exception already handled in the method itself, the problem is that the Task continues execution of the next line var links = ListArquivos.ListaLinksDownlaod(driver); which depends on the method AbrirDataAtual() to be executed, it also throws an exception. I have tried to treat within the method, put the task inside a Try / catch, but nothing works, there is always the exception in the method ListaLinksDownlaod and should not even get there.
How can I stop the execution of the task, such as when we send a CancellationToken, but this time, when an exception occurs.
private async Task<List<IWebElement>> Acessar(IWebDriver driver, string data, CancellationToken ct)
{
return await Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
LoginNgin.Login(config.User, config.Password, driver);
RelatoriosEstaticos.AbrirRelatoriosEstaticos(driver);
RelatoriosEstaticos.AbrirDataAtual(driver, data);
var links = ListArquivos.ListaLinksDownlaod(driver);
MethodInvoker action = delegate { pgbStatus.Maximum = links.Count(); };
pgbStatus.BeginInvoke(action);
return links;
});
}
It's not possible to tell for sure without seeing actual implementation of AbrirDataAtual, but it definitely looks like this method is handling exception that should not be handled there.
Generally, method should handle exception only if it can handle it properly (by properly I mean it can recover application to the state where program can safely continue, inform user about the error, etc.), otherwise it should not handle it at all and let the exception propagate to the caller(s) of the method.
Based on description of your problem, AbrirDataAtual doesn't (and can't) handle the exception properly, so you should not catch the exception there (or if excetion must be caught there, you should re-throw it). All following methods (including ListArquivos.ListaLinksDownlaod) will be skipped up to the point, where exception is handled. Problem solved!
The following example shows how to handle the exception directly in the task (after you remove exception handling in AbrirDataAtual). But it's likely that it's still not the best place for such exception handler, but again, finding such place would require complete source code so take it just as an example to clarify what I'm talking about:
private async Task<List<IWebElement>> Acessar(IWebDriver driver, string data, CancellationToken ct)
{
return await Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
LoginNgin.Login(config.User, config.Password, driver);
RelatoriosEstaticos.AbrirRelatoriosEstaticos(driver);
try
{
RelatoriosEstaticos.AbrirDataAtual(driver, data);
var links = ListArquivos.ListaLinksDownlaod(driver);
MethodInvoker action = delegate { pgbStatus.Maximum = links.Count(); };
pgbStatus.BeginInvoke(action);
return links;
}
catch (Exception)//Use more specific exception type if possible
{
//Do all neccesary to properly handle the exception
}
});
}
If you still believe that AbrirDataAtual method is the right place to handle the exception, an alternative approach is to modify AbrirDataAtual to return boolean flag indicating success/failure of it's operation, e.g.:
bool AbrirDataAtual(IWebDriver driver, string data)
{
try
{
//Do all the neccessary stuff
...
//Indicate that AbrirDataAtual succeeded
return true;
}
catch(Exception)
{
//Handle exception properly
...
//Indicate that AbrirDataAtual failed
return false;
}
}
private async Task<List<IWebElement>> Acessar(IWebDriver driver, string data, CancellationToken ct)
{
return await Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
LoginNgin.Login(config.User, config.Password, driver);
RelatoriosEstaticos.AbrirRelatoriosEstaticos(driver);
if (RelatoriosEstaticos.AbrirDataAtual(driver, data))
{
//Continue execution
var links = ListArquivos.ListaLinksDownlaod(driver);
MethodInvoker action = delegate { pgbStatus.Maximum = links.Count(); };
pgbStatus.BeginInvoke(action);
return links;
}
else
{
//AbrirDataAtual failed
return null;
//or throw exception if appropriate
throw new Exception();
}
});
}

C# event exception not caught in parent method

I`m working on implementing a get method for cache. This method will return to caller if a maximum wait time has passed(in my case 100ms for tests).
My issue is that the exception NEVER reaches the catch, after the timer triggered the event.
Please help me understand why? (I read that events are executed on the same thread, so that should`t be the issue)
public static T Get<T>(string key, int? maxMilisecondsForResponse = null)
{
var result = default(T);
try
{
// Return default if time expired
if (maxMilisecondsForResponse.HasValue)
{
var timer = new System.Timers.Timer(maxMilisecondsForResponse.Value);
timer.Elapsed += OnTimerElapsed;
timer.AutoReset = false;
timer.Enabled = true; // start the timer
}
var externalCache = new CacheServiceClient(BindingName);
Thread.Sleep(3000); // just for testing
}
catch (Exception ex)
{
// why is the exception not caught here?
}
return result;
}
private static void OnTimerElapsed(object source, System.Timers.ElapsedEventArgs e)
{
throw new Exception("Timer elapsed");
}
The timer fires on it's own thread. You can read more about it in this answer.
The answer to your question is to use async methods that can be cancelled. Then you can use a cancellation token source and do it the proper way instead of homebrewing a solution with timers.
You can find a good overview here.
For example:
cts = new CancellationTokenSource();
cts.CancelAfter(2500);
await Task.Delay(10000, cts.Token);
This would cancel the waiting task after 2500 (of 10000) because it took too long. Obviously you need to insert your own logic in a task instead of just waiting.
From MSDN
The Timer component catches and suppresses all exceptions thrown by
event handlers for the Elapsed event. This behavior is subject to
change in future releases of the .NET Framework.
And continues
Note, however, that this is not true of event handlers that execute
asynchronously and include the await operator (in C#) or the Await
operator (in Visual Basic). Exceptions thrown in these event handlers
are propagated back to the calling thread.
Please take a look Exception Handling (Task Parallel Library)
An applied example below:
public class Program
{
static void Main()
{
Console.WriteLine("Begin");
Get<string>("key", 1000);
Console.WriteLine("End");
}
public static T Get<T>(string key, int? maxMilisecondsForResponse = null)
{
var result = default(T);
try
{
var task = Task.Run(async () =>
{
await Task.Delay(maxMilisecondsForResponse.Value);
throw new Exception("Timer elapsed");
});
task.Wait();
}
catch (Exception ex)
{
// why the exception is not catched here?
Console.WriteLine(ex);
}
return result;
}
}
The timer is being executed in the own thread but you can't catch the exception at the caller level. So, it is not a good approach to use timer in this case and you can change it by creating the Task operation.
var result = default(T);
CacheServiceClient externalCache;
if (!Task.Run(() =>
{
externalCache = new CacheServiceClient(BindingName);
return externalCache;
}).Wait(100))//Wait for the 100 ms to complete operation.
{
throw new Exception("Task is not completed !");
}
// Do something
return result;

Handle Exceptions with tasks

Say that I have code like this:
serviceCallFinished = false;
Task.Factory.StartNew(() => {
response = ServiceManager.GeneralService.ServiceMethod(loginName, password);
}).ContinueWith(parentTask => { serviceCallFinished = true; });
while (!serviceCallFinished)
Thread.Sleep(100);
if (response.UserValid) { }
In this case the ServiceMethod will throw a Exception but this is never shown, instead I will get a null reference on the response.UserValid?
Why is the exception not handed over to my UI thread?
Note : This is on the UI thread and my idea is to not block the thread during the servicecall.
You don't need to use a flag like that just to wait for an async task to finish. Use Wait()
Task t = Task.Factory.StartNew(() => {
response = ServiceManager.GeneralService.ServiceMethod(loginName, password);
});
try
{
t.Wait();
}
catch (AggregateException e)
{
...
}
the Wait() call will elevate any exceptions within an overall AggregateException.
From the comments and contrary to the example posted, it seems like you want logic that returns to the caller while a response is being awaited, and then do a bunch of stuff afterwards. It also sounds like the stuff afterwards contains a lot of very complex methods that you want to write procedurally. The C# team has heard your problem and many like it and developed the async/await framework that will do exactly that, slated for C# 5. Alternatively, if it is an option for you, there is an AsyncCTP available here: http://www.microsoft.com/download/en/details.aspx?id=9983 HTH.
Look at How to: Handle Exceptions Thrown by Tasks
There is also a good sample.
Wha you could do is basically replace your sleeping loop with:
try
{
task.Wait();
}
catch (AggregateException ae)
{
...
}
Why don't you write code just like this:
Task.Factory.StartNew(() =>
{
response = ServiceManager.GeneralService.ServiceMethod(
loginName, password);
})
.ContinueWith(parentTask => { if (response.UserValid) { } });
It seems more clear and more responsive.

Categories

Resources