C# 返回任务而不是任务是否完全错误<;T>;?
任务API感觉有点像一个失败的坑,因为我可以用它做的每一件事都比do多DON。但是我正在尝试更多地使用它,我对C# 返回任务而不是任务是否完全错误<;T>;?,c#,async-await,C#,Async Await,任务API感觉有点像一个失败的坑,因为我可以用它做的每一件事都比do多DON。但是我正在尝试更多地使用它,我对async void的整个问题感到困惑 我的问题发生在我的应用程序永远使用本地数据文件的地方,因为使用了快速同步调用。现在,一些远程源是一个选项,它们有足够的延迟,我将全部切换为异步 所以我有一个方法,可能看起来像这样: void InitializeData() { SomeProperty = _dataSource.Load(...); } 对于低摩擦,我想做的简单的事情
async void
的整个问题感到困惑
我的问题发生在我的应用程序永远使用本地数据文件的地方,因为使用了快速同步调用。现在,一些远程源是一个选项,它们有足够的延迟,我将全部切换为异步
所以我有一个方法,可能看起来像这样:
void InitializeData() {
SomeProperty = _dataSource.Load(...);
}
对于低摩擦,我想做的简单的事情是:
Task InitializeDataAsync() {
return Task.Run(() => {
SomeProperty = _dataSource.Load(...);
});
}
但是关于使用async void
有多么糟糕的文章太多了,而且它们似乎随着返回任务的讨论而变得模糊不清。那么,我最好的选择是这样写:
Task<DataType> FetchDataAsync() {
return _dataSource.LoadAsync(...);
}
Task FetchDataAsync(){
返回_dataSource.LoadAsync(…);
}
…然后追捕所有来电者并让他们服从
我觉得人们的论点是,async void
是不好的,因为async Task
的不同行为,以及使用wait
时令人恐惧的未观察到的taskeexception。在这种情况下,我不太可能使用wait
。或者,如果我使用wait,它总是会有一个try/catch围绕它,因为我们已经对未被观察到的taskeexception产生了偏执
因此:
- “不使用
”是否与“不使用async void
”相同Task
- 如果您不使用
,它会减轻这种情况吗wait
- 如果您确实使用了
,是否总是用try/catch来包装它来缓解问题wait
异步无效
与返回任务不同。异步无效
的问题是无法观察任务异常(除了全局异常处理程序),也无法等待任务完成(实际上,您不能使用wait
异步void方法,因为没有任何东西可以wait
),这就是为什么应该避免它
返回
任务
没有这些问题,如果你等待它,你知道它什么时候完成,你等待的地方会出现异常,因此返回任务
是完全可以接受的。异步无效
与返回任务
不同。异步无效
的问题就在那里无法观察任务异常(全局异常处理程序除外),也无法等待任务完成(实际上,您不能使用await
异步void方法,因为没有任何东西可以await
),这就是为什么应该避免使用它
返回
任务
没有这些问题,如果你等待它,你知道它什么时候完成,在等待的地方会出现异常,所以返回任务
是完全可以接受的。看起来你只是困惑了。等待/异步API非常简单。唯一让人困惑的是理解。关于这一点,我将不作详细介绍,因为斯蒂芬·克利里的博客清楚地涵盖了这一点,但这里是重要的摘录:
要重复我上一篇文章中的一句话,请使用Task.Run调用CPU绑定的方法(从GUI线程)。不要使用它只是为了“提供我的异步方法可以使用的东西”
接下来
对于低摩擦,我想做的简单的事情是:
Task InitializeDataAsync() {
return Task.Run(() => {
SomeProperty = _dataSource.Load(...);
});
}
这是一个很明显的例子,将同步代码卸载到一个异步包装器中,实际上没有任何好处
但是有很多文章都提到使用async void是多么糟糕
这显然是notaasync void
方法。这是一种非异步Task
返回方法。以下是一种异步void方法:
async void InitializeDataAsync() {
await Task.Run(() => {
SomeProperty = _dataSource.Load(...);
});
}
他们似乎对返回任务的讨论模糊不清
我没有读过任何模糊这一信息的东西。这很简单;如果你有一个异步
方法,总是返回任务
或任务
(以下说明除外)。使用这两种方法(任务
或任务
)的区别纯粹是设计/架构
那么,我最好的选择是这样写:
Task<DataType> FetchDataAsync() {
return _dataSource.LoadAsync(...);
}
这里的观点是最好的。这两种方法都有效,不要忘记异步/等待
async Task<DataType> FetchDataAsync() {
return await _dataSource.LoadAsync(...);
}
async Task FetchDataAsync() {
_data = await _dataSource.LoadAsync(...);
}
异步任务FetchDataAsync(){
返回wait_dataSource.LoadAsync(…);
}
异步任务FetchDataAsync(){
_data=wait_dataSource.LoadAsync(…);
}
我觉得人们的论点是async void是不好的,因为async任务的不同行为以及使用Wait时令人恐惧的UnobservedTaskeException
:
摘录:
异步void方法具有不同的错误处理语义。当从异步任务或异步任务方法抛出异常时,将捕获该异常并将其放置在任务对象上。对于异步void方法,没有任务对象,因此从异步void方法抛出的任何异常都将直接在SynchronizationContext上引发在异步void方法启动时处于活动状态
所以
或者,如果我使用wait,它总是会有一个try/catch围绕它,因为我们已经对未被观察到的taskeexception产生了偏执
这意味着您无法通过try/catch绕过异步void捕获异常。这是一个巨大的问题
“不使用异步void”是否与“不使用任务”相同
这是苹果和橙子。是的:不要使用async void
,除非您将它们用于
不要使用任务,除非它们是CPU绑定的,或者您正在使用框架调用(或者您已经正确包装了自己使用的方法)
如果您不使用wait,它会减轻这种影响吗
希望到现在为止,您已经了解wait/async不同于任务,您要做的是编写一个