我被异步/等待宠坏了!现在正在努力与明确的任务延续

时间:2013-01-18 16:02:32

标签: c# multithreading xamarin.android task-parallel-library async-await

编辑(接受后)

它可能不会立即显现,但是@ Servy的答案是正确的而不需要在monodroid下自定义实现Unwrap - 在我的评论中我说它不存在,但它确实存在。

结束修改

我正在编写一堆使用我们的RESTful Web服务的应用程序,尽管我认为我知道如何正确使用任务但事实并非如此。方案是我已经为Windows Store实现了代码 - 使用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) - 感觉有点像我疯了在这里 - 非常感谢任何帮助。

3 个答案:

答案 0 :(得分:5)

所以,首先,你的第二个和第三个实现的非异步/等待的重复是你应该做的,即使你有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;
}

答案 1 :(得分:0)

对于完全支持的方法,最好等待MonoDroid的官方异步/等待支持 - Xamarin已经说过“即将推出” - 今年上半年。

然而,如果你感觉更冒险......那么至少有几个人设法让异步/等待在MonoDroid中工作:

答案 2 :(得分:-1)

这就是我在.net 4.0(VS 2010)中处理异步任务所做的工作。有些人说你不需要调用task.Wait();而其他人已经说过,是的,你确实需要调用task.Wait();我认为不同的是task.Wait()是否隐含/封装在{{1属性。

我坚持使用更明确的方法来调用task.Result

这将阻止,直到通话完成,但如果您不使用4.5,我不确定还有哪些其他选项可用。