【问题标题】:Catch the exception of an action executed by a task (Task.Delay())捕获任务执行的动作的异常(Task.Delay())
【发布时间】:2021-04-30 14:26:45
【问题描述】:

我构建了一种计划任务,它将在计划日期执行操作。我用Task.Delay(delay).ContinueWith(action.Invoke())

有时该操作会引发异常,但仍无法传播此异常以正确注册它。

在我的示例中,a 创建了一个包含三个文本的列表,空文本将引发异常,但是我无法在 try..catch 块中捕获此异常。

class Program
    {
        static async Task Main()
        {
            var texts = new List<string>() { "text 1", null, "text 2"};
            foreach (var text in texts)
            {
                var scheduleDate = DateTime.UtcNow.AddSeconds(10);
                try
                {
                    ReminderControl.AddReminder(Print, text, scheduleDate);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error to schedule the text {text} to be print on {scheduleDate:s}. ex: {ex.Message}");
                }                
            }

            while (true)
            {
                await Task.Delay(2000);
                Console.WriteLine("Doing other things...");
            }
        }

        static void Print(object text)
        {
            if (text is null)
                throw new ArgumentNullException(); \\thrown , but not propagated out

            Console.WriteLine(text);
        }
    }

    public class ReminderControl
    {
        public static void AddReminder(Action<object> remiderCallback, object context, DateTime scheduleDate)
        {
            var delay = scheduleDate.Subtract(DateTime.UtcNow);
            Task.Delay(delay)
              .ContinueWith(t => remiderCallback.Invoke(context));
            //.ContinueWith(t => throw t.Exception, TaskContinuationOptions.OnlyOnFaulted); even with this line the exception still does not propagate
        }
    }

在 Task.Delay 中使用 await 更新

await ReminderControl.AddReminder(Print, text, scheduleDate);

public static async Task AddReminder(Action<object> remiderCallback, object context, DateTime scheduleDate)
{
    var delay = scheduleDate.Subtract(DateTime.UtcNow);
    await Task.Delay(delay);
    remiderCallback.Invoke(context);
}

之后,try..catch 块捕获了异常,但是,"Doing other things..." 停止打印,可能正在等待 AddRemind 完成。

我想让"Doing other things..." 保持异步打印。

【问题讨论】:

  • 使用第二种方法,您可以收集AddReminder返回的所有任务,然后使用await Task.WhenAll(tasks)等待它们全部完成。
  • 您是否可以将 try catch 块放在您的 AddReminder 方法中并使用 await 处理那里的事情,但在更高级别上不使用 await 以便您可以继续执行其他操作想要吗?
  • 是的。有可能,但是当 AddReminder 动作抛出异常时,仍然需要传播出去。

标签: c# .net


【解决方案1】:

如果您希望异常从任务中正确冒出,您需要使用完整的async 编译器构造。将您的表达式替换为:

await Task.Delay(delay);
action.Invoke();

将它放入一个返回某种Taskasync 函数中,并确保等待返回的任务。该 await 将冒泡您可以捕获的异常。

【讨论】:

  • 谢谢。在Task.Delay中添加await并在foreach中等待AddReminder后,我在主线程中的“Doing other things...”停止打印,可能正在等待AddReminder。有什么猜测吗?
  • 猜猜看,你的分析是正确的。如果您想问如何使它仍然是异步的,同时保持try..catch 围绕它,请将try..catch 块和等待的AddReminder 调用放在单独的@987654332 中@ 函数并在不等待的情况下调用该函数。
  • 是的,没错,我需要在外面继续异步工作。 AddReminder 就像“一劳永逸”,但如果有任何问题,请记录它。关于你的最后一个建议,我对这两个try..catch 有点困惑。所以我正在尝试实施它。谢谢。
  • 我失败得很惨,我相信我不明白你的解决方案。你能用那个更新你的答案还是看看我错在哪里? dotnetfiddle.net/nyV7C5
  • @RafaelPimenta,试试这样的:dotnetfiddle.net/oekZHb。这总体上不是一个好的解决方案,一个更好的解决方案涉及一个依赖注入的日志服务,该服务会在未处理的异常上被调用,但这个实现更符合您的项目。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-23
  • 2013-08-17
  • 2022-10-02
相关资源
最近更新 更多