等待循环中的异步方法完成
我有一个C#程序,当前同时从几个站点下载数据,之后代码对我已下载的数据做了一些处理。我正试图移动这个来异步执行我的下载,然后处理我下载的数据。我在测序时遇到了一些麻烦。以下是我正在使用的代码的快照:等待循环中的异步方法完成
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Started URL downloader");
UrlDownloader d = new UrlDownloader();
d.Process();
Console.WriteLine("Finished URL downloader");
Console.ReadLine();
}
}
class UrlDownloader
{
public void Process()
{
List<string> urls = new List<string>() {
"http://www.stackoverflow.com",
"http://www.microsoft.com",
"http://www.apple.com",
"http://www.google.com"
};
foreach (var url in urls)
{
WebClient Wc = new WebClient();
Wc.OpenReadCompleted += new OpenReadCompletedEventHandler(DownloadDataAsync);
Uri varUri = new Uri(url);
Wc.OpenReadAsync(varUri, url);
}
}
void DownloadDataAsync(object sender, OpenReadCompletedEventArgs e)
{
StreamReader k = new StreamReader(e.Result);
string temp = k.ReadToEnd();
PrintWebsiteTitle(temp, e.UserState as string);
}
void PrintWebsiteTitle(string temp, string source)
{
Regex reg = new Regex(@"<title[^>]*>(.*)</title[^>]*>");
string title = reg.Match(temp).Groups[1].Value;
Console.WriteLine(new string('*', 10));
Console.WriteLine("Source: {0}, Title: {1}", source, title);
Console.WriteLine(new string('*', 10));
}
}
基本上,我的问题是这样的。从上面我的输出是:
Started URL downloader
Finished URL downloader
"Results of d.Process()"
我想要做的就是完成d.Process()方法,然后返回到我的计划类“主”的方法。所以,我在找的输出是:
Started URL downloader
"Results of d.Process()"
Finished URL downloader
我d.Process()方法异步运行,但我无法弄清楚如何等待我所有的处理,以恢复我的主要方法之前完成。关于如何在C#4.0中做到这一点的任何想法?我不知道如何去'告诉'我的Process()方法等到它的所有异步活动完成后才返回Main方法。
如果你是在.NET> = 4.0,你可以使用TPL
Parallel.ForEach(urls, url =>
{
WebClient Wc = new WebClient();
string page = Wc.DownloadString(url);
PrintWebsiteTitle(page);
});
我也将使用HtmlAgilityPack,而不是解析正则表达式的页面。
void PrintWebsiteTitle(string page)
{
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(page);
Console.WriteLine(doc.DocumentNode.Descendants("title").First().InnerText);
}
谢谢@ L.B这个很棒!我是新来的异步编程,所以不熟悉TPL。 – armohan 2012-07-13 17:28:08
我会推荐使用WebClient.DownloadDataAsync而不是自己写。然后,您可以使用任务并行库来包装调用DownloadDataAsync在TaskCompletionSource获得多个任务对象,你可以等待或继续:
webClient.DownloadDataAsync(myUri);
webClient.DownloadDataCompleted += (s, e) =>
{
tcs.TrySetResult(e.Result);
};
if (wait)
{
tcs.Task.Wait();
Console.WriteLine("got {0} bytes", tcs.Task.Result.Length);
}
else
{
tcs.Task.ContinueWith(t => Console.WriteLine("got {0} bytes", t.Result.Length));
}
处理错误情况,可以扩大使用TaskCompletionSource的:
webClient.DownloadDataCompleted += (s, e) =>
{
if(e.Error != null) tcs.SetException(e.Error);
else if(e.Cancelled) tcs.SetCanceled();
else tcs.TrySetResult(e.Result);
};
做同样多的任务:
Task.WaitAll(tcs.Task, tcs2.Task);
或
Task.Factory.ContinueWhenAll(new Task[] {tcs.Task, tcs2.Task}, ts =>
{
/* do something with all the results */
});
关于异步操作存在多个问题:一个示例http://stackoverflow.com/questions/6906778/how-to-wait-on-multiple-asynchronous-operation-completion – IAbstract 2012-07-13 17:06:08
您使用的是什么版本的C#? .Net 4.0使用Task对象提供TPL。 – IAbstract 2012-07-13 17:07:24
您可以执行OpenRead,它会同步执行并阻止当前线程。 – 2012-07-13 17:13:19