简体   繁体   中英

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");
}

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 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.

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM