如果您想使用async
andawait
关键字(虽然您不必这样做,但它们确实使 .NET 4.5 中的事情变得更容易),您首先需要更改您的方法以使用关键字ScrapeData
返回一个Task<T>
实例,如下所示:async
async Task<DataSet> ScrapeDataAsync(Uri url)
{
// Create the HttpClientHandler which will handle cookies.
var handler = new HttpClientHandler();
// Set cookies on handler.
// Await on an async call to fetch here, convert to a data
// set and return.
var client = new HttpClient(handler);
// Wait for the HttpResponseMessage.
HttpResponseMessage response = await client.GetAsync(url);
// Get the content, await on the string content.
string content = await response.Content.ReadAsStringAsync();
// Process content variable here into a data set and return.
DataSet ds = ...;
// Return the DataSet, it will return Task<DataSet>.
return ds;
}
请注意,您可能希望远离WebClient
该类,因为它本身不支持Task<T>
其异步操作。.NET 4.5 中更好的选择是HttpClient
类。我选择使用HttpClient
上面。此外,请查看HttpClientHandler
class,特别是您将用于在每个请求中发送 cookie的CookieContainer
属性。
但是,这意味着您很可能必须使用await
关键字来等待另一个异步操作,在这种情况下,很可能是页面的下载。您必须定制下载数据的调用以使用异步版本和await
那些。
一旦完成,你通常会调用await
它,但在这种情况下你不能这样做,因为你会调用await
一个变量。在这种情况下,您正在运行一个循环,因此每次迭代都会重置该变量。在这种情况下,最好将 存储Task<T>
在一个数组中,如下所示:
DataSet alldata = ...;
var tasks = new List<Task<DataSet>>();
foreach(var url in the8000urls)
{
// ScrapeData downloads the html from the url with
// WebClient.DownloadString
// and scrapes the data into several datatables which
// it returns as a dataset.
tasks.Add(ScrapeDataAsync(url));
}
存在将数据合并到allData
. 为此,您希望在返回的实例上调用该ContinueWith
方法Task<T>
并执行将数据添加到的任务allData
:
DataSet alldata = ...;
var tasks = new List<Task<DataSet>>();
foreach(var url in the8000urls)
{
// ScrapeData downloads the html from the url with
// WebClient.DownloadString
// and scrapes the data into several datatables which
// it returns as a dataset.
tasks.Add(ScrapeDataAsync(url).ContinueWith(t => {
// Lock access to the data set, since this is
// async now.
lock (allData)
{
// Add the data.
}
});
}
然后,您可以使用类上的WhenAll
方法等待所有任务:Task
await
// After your loop.
await Task.WhenAll(tasks);
// Process allData
但是,请注意,您有一个foreach
, 并且WhenAll
需要一个IEnumerable<T>
实现。这是一个很好的指标,表明它适合使用 LINQ,它是:
DataSet alldata;
var tasks =
from url in the8000Urls
select ScrapeDataAsync(url).ContinueWith(t => {
// Lock access to the data set, since this is
// async now.
lock (allData)
{
// Add the data.
}
});
await Task.WhenAll(tasks);
// Process allData
如果你愿意,你也可以选择不使用查询语法,在这种情况下没关系。
请注意,如果包含方法未标记为async
(因为您在控制台应用程序中并且必须在应用程序终止之前等待结果),那么您可以在调用时简单地调用返回的Wait
方法:Task
WhenAll
// This will block, waiting for all tasks to complete, all
// tasks will run asynchronously and when all are done, then the
// code will continue to execute.
Task.WhenAll(tasks).Wait();
// Process allData.
也就是说,关键是,您希望将Task
实例收集到一个序列中,然后在处理之前等待整个序列allData
。
allData
但是,如果可以的话,我建议在合并之前尝试处理数据;除非数据处理需要整个 .DataSet
_