1

現在、複数のサイトから同期的にデータをダウンロードする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()メソッドを完了してから、Programクラスの「Main」メソッドに戻ることです。したがって、私が探している出力は次のとおりです。

Started URL downloader
"Results of d.Process()"
Finished URL downloader

d.Process()メソッドは非同期で実行されますが、すべての処理が完了するのを待ってからMainメソッドに戻る方法がわかりません。C#4.0でこれを行う方法についてのアイデアはありますか?Process()メソッドに、すべての非同期アクティビティが完了するまで待機してからMainメソッドに戻るように「指示」する方法がわかりません。

4

2 に答える 2

8

.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);
}
于 2012-07-13T17:15:08.730 に答える
0

独自に作成する代わりに、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 */
                                                    });
于 2012-07-13T17:31:14.640 に答える