返回Task而不是Task <t>是完全错误的吗?

时间:2018-02-27 21:58:55

标签: c# async-await

任务API感觉有点像失败,考虑到我可以用它做的每件事情都有更多 DON&#39; T 而不是 DO 。但是我试图更多地使用它,我对async void的整个问题感到困惑。

我的问题发生在我的应用程序永远使用本地数据文件的地方,并且因为它使用了快速同步调用。现在一些远程源是一个选项,它们有足够的延迟我将它们全部切换为异步。

所以我有一个方法可能看起来像这样:

void InitializeData() {
    SomeProperty = _dataSource.Load(...);
}

我想为低摩擦做的天真的事情是:

Task InitializeDataAsync() {
    return Task.Run(() => {
        SomeProperty = _dataSource.Load(...);
    });
}

但是有很多关于使用async void是多么可怕的文章,并且他们似乎在讨论返回任务时模糊不清。因此,我最好的选择是:

Task<DataType> FetchDataAsync() {
    return _dataSource.LoadAsync(...);
}

...然后追捕所有来电者并让他们遵守?

我觉得人们提出的论点是async void是不好的,因为async Task的不同行为和使用await时可怕的UnobservedTaskException。在这种情况下,我不太可能使用await。或者,如果我确实使用await,它总是会有一个try / catch,因为我们已经对UnobservedTaskException产生了偏执。

所以:

  • 不是&#34;不要使用async void&#34;和#34;不要使用Task
  • 如果您不使用await,是否会减轻这种影响?
  • 如果你使用await,总是用try / catch包装它可以缓解这个问题吗?

2 个答案:

答案 0 :(得分:9)

async void与返回Task不同。 async void的问题是无法观察任务异常(除了全局异常处理程序之外),也无法等待任务完成(实际上你不能await async void方法,因为await没有任何内容,因此应该避免这种情况。

返回Task没有这些问题,如果你知道它完成时await,你会得到例外,等待返回Task完全可以接受。

答案 1 :(得分:2)

看起来你只是感到困惑。 await / async API非常简单。唯一令人困惑的部分是理解the difference between await/async API and the Task API。我不会详细了解这一点,因为Stephen Cleary的博客清楚地涵盖了这一点,但这里有重要的摘录:

  

要重申我上一篇文章中的一句话,请使用Task.Run调用CPU绑定方法(来自GUI线程)。不要仅仅使用它来“为我的异步方法提供可供使用的东西”。

...下

  

我想为低摩擦做的天真的事情是:

Task InitializeDataAsync() {
  return Task.Run(() => {
    SomeProperty = _dataSource.Load(...);
  });
}

这是将Sync代码卸载到Async包装器中的一个明显案例,它实际上没有任何好处。

  

但是有很多关于使用async void有多糟糕的文章,

这显然是 async void方法。这是一种非异步Task返回方法。这是一个异步void方法:

async void InitializeDataAsync() {
  await Task.Run(() => {
    SomeProperty = _dataSource.Load(...);
  });
}
  

他们似乎在讨论返回任务时模糊不清。

我没有看到任何模糊这些信息的内容。这很简单;如果您使用async方法,请始终返回TaskTask<T>(除非如下所述)。使用这两个(TaskTask<T>)之间的区别纯粹是设计/架构。

  

所以我最好的选择就是:

Best在这里真的是一个意见。这两项都有效,并且不要忘记 async/wait

async Task<DataType> FetchDataAsync() {
  return await _dataSource.LoadAsync(...);
}

async Task FetchDataAsync() {
  _data = await _dataSource.LoadAsync(...);
}
  

我觉得人们提出的论点是async void很糟糕,因为async Task的行为不同,而且当你使用await时可怕的UnobservedTaskException。

The most important difference is how exceptions are handled

摘录:

  

Async void方法具有不同的错误处理语义。当异步任务或异步任务方法抛出异常时,将捕获该异常并将其放在Task对象上。使用async void方法时,没有Task对象,因此异步void方法抛出的任何异常都将直接在async void方法启动时处于活动状态的SynchronizationContext上引发。

所以

  

或者,如果我确实使用await,它总是会有一个try / catch,因为我们已经对UnobservedTaskException产生了偏执。

这意味着您无法通过try / catch绕过异步void来捕获异常。这是个大问题!

  

是不是&#34;不要使用async void&#34;和#34;不要使用任务?&#34;

这是苹果和橘子。是的:除非您将async void用于async event handlers,否则请勿使用 String a = "545F6 6 100"; String b = "12N45 A 50"; List<String> results = new ArrayList<>(); // here you will store matching numbers for(String str : a.split("\\s+")) { // for each String that you get after splitting source String at whitespace characters... if(str.matches("\\b[\\d]+\\b")) { //check if that String matches given pattern: word boundary-only digits-word boundary results.add(str); // it there is a match, add this String to results ArrayList } else { System.out.println("The number " + str + " is invalid"); } } System.out.println("Valid numbers: " + Arrays.toString(results.toArray())); // just to print results results.clear(); System.out.println(); for(String str : b.split("\\s+")) { if(str.matches("\\b[\\d]+\\b")) { results.add(str); } else { System.out.println("The number " + str + " is invalid"); } } System.out.println("Valid numbers: " + Arrays.toString(results.toArray()));

不要使用任务,除非他们受CPU限制或您正在使用框架调用(或者您已正确包装自己使用IO Completion Ports的方法)。

  

如果你没有使用await,它会减轻吗?

希望现在您了解await / async与Task不同,您尝试做的是编写所有等待/异步代码。

  

如果你确实使用了await,那么总是用try / catch包装它可以缓解这个问题吗?

取决于上述情况。