在父方法中未捕获C#事件异常

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在父方法中未捕获C#事件异常相关的知识,希望对你有一定的参考价值。

我正在努力实现缓存的get方法。如果已经过了最大等待时间,则此方法将返回调用者(在我的情况下为100ms进行测试)。

我的问题是,在计时器触发事件​​后,异常永远不会到达捕获。

请帮我理解为什么? (我读到事件是在同一个线程上执行的,所以这应该是问题)

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

计时器触发它自己的线程。你可以在this answer上阅读更多相关信息。

您的问题的答案是使用可以取消的异步方法。然后,您可以使用取消令牌源并以正确的方式执行,而不是使用计时器自行创建解决方案。

你可以找到一个很好的概述here

例如:

cts = new CancellationTokenSource();  

cts.CancelAfter(2500);  

await Task.Delay(10000, cts.Token);

这将取消2500(10000)之后的等待任务,因为它花了太长时间。显然,您需要在任务中插入自己的逻辑而不是等待。

另一答案

来自MSDN

Timer组件捕获并抑制事件处理程序为Elapsed事件抛出的所有异常。在将来的.NET Framework版本中,此行为可能会发生更改。

并继续

但请注意,异步执行的事件处理程序并不包括await运算符(在C#中)或Await运算符(在Visual Basic中)。在这些事件处理程序中抛出的异常会传播回调用线程。

请看看Exception Handling (Task Parallel Library)

下面的应用示例:

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;
    }
}
另一答案

timer正在自己的线程中执行,但你不能在调用者级别捕获异常。因此,在这种情况下使用计时器不是一个好方法,您可以通过创建Task操作来更改它。

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;

以上是关于在父方法中未捕获C#事件异常的主要内容,如果未能解决你的问题,请参考以下文章

在父活动中从子活动中捕获异常

在 MFC VC++ 中未捕获 MouseWheel 事件

Tomcat 中未捕获的异常打印到 localhost.[date].log 而不是 catalina.out

如何在父 li 中捕获子复选框单击事件?

来自第 3 方静态库的回调中未捕获的异常

Java子线程中的异常处理(通用)