Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/271.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么在使用async await时,不管观察结果如何,都会抛出UnobservedTaskException?_C#_.net_Task_Task Parallel Library - Fatal编程技术网

C# 为什么在使用async await时,不管观察结果如何,都会抛出UnobservedTaskException?

C# 为什么在使用async await时,不管观察结果如何,都会抛出UnobservedTaskException?,c#,.net,task,task-parallel-library,C#,.net,Task,Task Parallel Library,以下场景在.NET 4.5下运行,因此任何未观察到的taskeexception都不会出现 我有一个习惯,就是在我的应用程序开始时执行此命令,从而监听任何引发的未观察到的taskeexception: private void WatchForUnobservedTaskExceptions() { TaskScheduler.UnobservedTaskException += (sender, args) => { args.Exception.Dump("Ooop

以下场景在.NET 4.5下运行,因此任何
未观察到的taskeexception
都不会出现

我有一个习惯,就是在我的应用程序开始时执行此命令,从而监听任何引发的
未观察到的taskeexception

private void WatchForUnobservedTaskExceptions()
{
  TaskScheduler.UnobservedTaskException += (sender, args) =>
  {
      args.Exception.Dump("Ooops");
  };
}
当我想显式忽略任务引发的任何异常时,我还有一个助手方法:

public static Task IgnoreExceptions(Task task) 
  => task.ContinueWith(t =>
      {
          var ignored = t.Exception.Dump("Checked");
      },
      CancellationToken.None,
      TaskContinuationOptions.ExecuteSynchronously,
      TaskScheduler.Default);
因此,如果我执行以下代码:

void Main()
{
  WatchForUnobservedTaskExceptions();

  var task = Task.Factory.StartNew(() =>
  {
      Thread.Sleep(1000);
      throw new InvalidOperationException();
  });

  IgnoreExceptions(task);

  GC.Collect(2);
  GC.WaitForPendingFinalizers();

  Console.ReadLine();    
}
在我们从
控制台返回.ReadLine()
后,我们将不会看到抛出任何
未观察到的taskeException
,这是我们所期望的

但是,如果我将上述
任务
更改为开始使用
async/await
,其他所有操作与之前相同:

var task = Task.Factory.StartNew(async () =>
{
    await Task.Delay(1000);
    throw new InvalidOperationException();
});
现在我们得到抛出的
unobservedtaskeexception
。调试代码显示继续执行时
t.Exception
null

如何在这两种情况下正确忽略异常?

使用

var task = Task.Factory.StartNew(async () =>
{
    await Task.Delay(1000);
    throw new InvalidOperationException();
}).Unwrap();

请参阅关于将Task.Factory.StartNew与异步修饰符一起使用

通过在此处使用async关键字,编译器将此委托映射为
Func
:调用委托将返回
任务
,以表示此调用的最终完成。由于委托是
Func
TResult
Task
,因此“t”的类型将是
Task
,而不是
Task

为了处理这类情况,在.NET4中,我们引入了展开方法

更多

为什么不使用Task.Factory.StartNew?

。。不理解异步委托。问题是,当您将异步委托传递给StartNew时,很自然地会假定返回的任务代表该委托。但是,由于StartNew不理解异步委托,因此该任务实际表示的只是该委托的开始。这是编码器在异步代码中使用StartNew时遇到的第一个陷阱之一

编辑

var task=task.Factory.StartNew(异步(…)
=>中的
task
类型实际上是
task
。您必须
展开它才能获得源任务。考虑到这一点:

您只能在
任务>
上调用
展开
,因此可以向
忽略异常
添加重载,以适应以下情况:

void Main()
{
    WatchForUnobservedTaskExceptions();

    var task = Task.Factory.StartNew(async () =>
    {
        await Task.Delay(1000);
        throw new InvalidOperationException();
    });

    IgnoreExceptions(task);

    GC.Collect(2);
    GC.WaitForPendingFinalizers();

    Console.ReadLine();
}

private void WatchForUnobservedTaskExceptions()
{
    TaskScheduler.UnobservedTaskException += (sender, args) =>
    {
        args.Exception.Dump("Ooops");
    };
}

public static Task IgnoreExceptions(Task task)
  => task.ContinueWith(t =>
      {
          var ignored = t.Exception.Dump("Checked");
      },
      CancellationToken.None,
      TaskContinuationOptions.ExecuteSynchronously,
      TaskScheduler.Default);


public static Task IgnoreExceptions(Task<Task> task)
=> task.Unwrap().ContinueWith(t =>
{
    var ignored = t.Exception.Dump("Checked");
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
void Main()
{
监视未观察到的异常();
var task=task.Factory.StartNew(异步()=>
{
等待任务。延迟(1000);
抛出新的InvalidOperationException();
});
忽略异常(任务);
GC.Collect(2);
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
私有void watchforunobservedtaskeexceptions()
{
TaskScheduler.UnobservedTaskException+=(发送方,参数)=>
{
参数异常转储(“Ooops”);
};
}
公共静态任务忽略异常(任务任务)
=>task.ContinueWith(t=>
{
忽略var=t.Exception.Dump(“选中”);
},
取消令牌。无,
TaskContinuationOptions.Executes同步执行,
TaskScheduler.Default);
公共静态任务忽略异常(任务任务)
=>task.Unwrap().ContinueWith(t=>
{
忽略var=t.Exception.Dump(“选中”);
},
取消令牌。无,
TaskContinuationOptions.Executes同步执行,
TaskScheduler.Default);

var
Task
以及
Task
的相互关系结合起来,掩盖了这个问题。如果我稍微重写一下代码,问题就显而易见了

  Task<int> task1 = Task.Factory.StartNew(() =>
  {
     Thread.Sleep(1000);
     throw new InvalidOperationException();
     return 1;
  });

  Task<Task<int>> task2 = Task.Factory.StartNew(async () =>
  {
     await Task.Delay(1000);
     throw new InvalidOperationException();
     return 1;
  });
Task task1=Task.Factory.StartNew(()=>
{
睡眠(1000);
抛出新的InvalidOperationException();
返回1;
});
tasktask2=Task.Factory.StartNew(异步()=>
{
等待任务。延迟(1000);
抛出新的InvalidOperationException();
返回1;
});

这更好地说明了Peter Bons所说的。

为什么不捕获异步函数中的异常呢。这样就不会出现未观察到的任务异常。helper方法的要点是避免在任何地方都进行捕获。这种情况到底有多现实?你不应该在
Task.Run
Task.Factory.StartNew
@PeterBons中包装一个已经异步的方法哦,公平地说,这是有用途的;不一定有用,但是sometimes@MarcGravell
Dump()
任务的一种方法。Run()
不是选项,因为
IgnoreExceptions
位于库中,不知道
任务的来源。我似乎无法在
IgnoreException
中使用
Unwrap
,它无法编译。谢谢,这有助于更好地说明问题。
  Task<int> task1 = Task.Factory.StartNew(() =>
  {
     Thread.Sleep(1000);
     throw new InvalidOperationException();
     return 1;
  });

  Task<Task<int>> task2 = Task.Factory.StartNew(async () =>
  {
     await Task.Delay(1000);
     throw new InvalidOperationException();
     return 1;
  });