使用TPL与现有的异步API

8

我希望能够在现有的API中使用TPL,特别是RestSharp,以便可以使用continuations。

但这意味着我必须包装一个不采用经典的.NET异步方法,而是实现回调的API。例如以下代码:

var client = new RestClient("service-url");
var request = new RestRequest();

client.ExecuteAsync<List<LiveTileWeatherResponse>>(request, 
    (response) =>
    {
        ...
    });

我希望能够在可能的情况下,使用TPL包装ExecuteAsync。但是我却无论如何都想不出如何做到这一点。

有什么想法吗?


TaskCompletionSource 能和 RestSharp 一起使用吗? - Derek Beattie
是的,的确如此。(这就是我接受答案的原因) - Claus Jørgensen
2个回答

12

TPL提供了TaskCompletionSource类,使您可以将几乎任何内容暴露为任务。通过调用SetResultSetException,您可以使任务成功或失败。在您的示例中,您可能可以执行以下操作:

static Task<T> ExecuteTask<T>(this RestClient client, RestRequest request)
{
    var tcs = new TaskCompletionSource<T>();
    client.ExecuteAsync<T>(request, response => tcs.SetResult(response));
    return tcs.Task;
}

你可以随后使用它:

var task = client.ExecuteTask<List<LiveTileWeatherResponse>>(request);
foreach (var tile in task.Result)
{}

或者,如果你想要链接任务:

var task = client.ExecuteTask<List<LiveTileWeatherResponse>>(request);
task.ContinueWith(
    t => 
    {
        foreach (var tile in t.Result)
        {}
    }
);

您可以在http://blogs.msdn.com/b/pfxteam/archive/2009/06/02/9685804.aspx上了解有关TaskCompletionSource的更多信息。


这绝对是正确的方法。但不幸的是,ContinueWith被过早地调用了,而t.IsCompleted被设置为false :( - Claus Jørgensen
不用担心 - 在ContinueWith中指定的委托将在任务完成后被调度和执行,无论何时。但是,在委托内部,t.IsCompleted应始终为“true”...你是说你在委托内看到它为“false”吗? - Bojan Resnik
1
问题似乎在于我调用了.Start(),而WP7的Mono实现并没有警告我这是不合法的。(我使用.NET 4.0控制台应用程序进行测试,它告诉我这是错误的)。 - Claus Jørgensen
我犯了和@Claus一样的错误——在MonoTouch应用程序中调用了Start(),因此任务在TaskCompletionSource有机会设置其结果之前就已经完成了。 - Dan Abramov

1

这也是我学习TPL时的一个主要痛点。

你需要的是TaskCompletionSource。当你创建一个TaskCompletionSource时,它会创建一个特殊的Task对象(可以通过TaskCompletionSource.Task属性访问),只有在调用相关TaskCompletionSource上的SetResultSetException方法时才会完成。

本文解释了如何使用TPL包装APM操作(还有Rx)。另请参见此代码片段,其中演示了一个被TPL包装的APM操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接