I';我被async/await破坏了!现在正在为显式的任务延续而挣扎
本文关键字:挣扎 延续 任务 我被 async 坏了 await | 更新日期: 2023-09-27 17:54:28
编辑(验收后(
这可能不太明显,但@Servy的答案是正确的,不需要在monodroid下自定义实现Unwrap
——在我的评论中,我说它不存在,但它确实存在。
结束编辑
我正在写一堆使用RESTful web服务的应用程序,尽管我认为我知道如何正确使用任务,但事实证明我不知道。场景是,我已经为Windows应用商店实现了代码——使用async/await
,我需要为MonoDroid实现一些几乎相同的东西——但它没有(没有一些我不想使用的构建技巧(。
我已经将这个问题的问题归结为一组简单的任务,用于异步获取一个整数,然后,在继续中,启动另一个异步任务,该任务将转换从该整数构建的字符串。在C#5中,这将是:
注意-当然,我在这里使用Task.FromResult<T>
来代替实际的异步代码
private async Task<string> GetStringAsync()
{
return await GetStringFromIntAsync(await GetIntAsync());
}
private async Task<int> GetIntAsync()
{
return await Task.FromResult(10);
}
private async Task<string> GetStringFromIntAsync(int value)
{
return await Task.FromResult(value.ToString());
}
为了将其转换为基于延续的模式,我尝试了以下操作:
private Task<string> GetStringAsync()
{
//error on this line
return GetIntAsync().ContinueWith(t => GetStringFromIntAsync(t.Result));
}
private Task<int> GetIntAsync()
{
return Task.FromResult(10);
}
private Task<string> GetStringFromIntAsync(int value)
{
return Task.FromResult(value.ToString());
}
然而,这是不正确的,因为GetStringFromIntAsync
方法返回Task<string>
,这意味着延续最终返回Task<Task<string>>
而不是Task<string>
。
然而,我发现显式等待GetStringFromIntAsync
方法确实有效:
private Task<string> GetStringAsync()
{
return GetIntAsync().ContinueWith(t =>
{
var nested = GetStringFromIntAsync(t.Result);
nested.Wait();
return nested.Result;
});
}
然而,问题是——这是正确的方法吗——我可以不返回调用者等待的某种延续吗?
我已经使用了很多任务,但没有像这样将不同类型的任务链接在一起(当然除了async
/await
(,我觉得自己在这里有点疯了,所以非常感谢任何帮助。
因此,首先,即使您有async/await,您也应该执行第二个和第三个实现的非async/await实现。使用它只会增加一些不必要的开销。
对于第一种情况,不,您不想使用Wait
。这将阻塞线程,而不是允许它返回到池中。你只需要打开任务,得到它的内部任务。您可以使用Unwrap
方法来做到这一点:
private Task<string> GetStringAsync()
{
return GetIntAsync()
.ContinueWith(t => GetStringFromIntAsync(t.Result))
.Unwrap();
}
这里有一个Unwrap
函数,如果没有可用的话,您可以使用它。
public static Task<T> Unwrap<T>(this Task<Task<T>> task)
{
var tcs = new TaskCompletionSource<T>();
task.ContinueWith(t =>
{
t.Result.ContinueWith(innerT => tcs.SetResult(innerT.Result)
, TaskContinuationOptions.OnlyOnRanToCompletion);
t.Result.ContinueWith(innerT => tcs.SetCanceled()
, TaskContinuationOptions.OnlyOnCanceled);
t.Result.ContinueWith(innerT => tcs.SetException(innerT.Exception.InnerExceptions)
, TaskContinuationOptions.OnlyOnRanToCompletion);
}
, TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t => tcs.SetCanceled()
, TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t => tcs.SetException(t.Exception.InnerExceptions)
, TaskContinuationOptions.OnlyOnFaulted);
return tcs.Task;
}
对于一种完全支持的方法,最好等待MonoDroid的官方async/await支持——Xamarin说这"很快就会到来"——今年上半年。
然而,如果你觉得更冒险。。。然后,至少有几个人成功地在MonoDroid:中实现了异步/等待工作
- 使用"Full Mono"-http://fizzylogic.nl/2012/05/04/adding-asyncawait-support-to-your-mono-for-android-projects/
- 使用"Microsoft BCL"-查看推特搜索示例和Daniel Plasted在BUILD的演讲中的代码http://channel9.msdn.com/Events/Build/2012/3-004
这就是我在.net 4.0(VS 2010(中处理异步任务所做的。有些人说你不需要打task.Wait();
,另一些人说,是的,你确实需要打task.Wait();
。我认为区别在于任务是否完成。Wait((隐含/封装在task.Result
属性中。
我坚持使用更明确的方法调用task.Wait();
。
这将被阻止,直到调用完成,但我不确定如果您不使用4.5,还有什么其他选项可用。