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(,我觉得自己在这里有点疯了,所以非常感谢任何帮助。

I';我被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,还有什么其他选项可用。