0

私は 2000 回以上の URL 呼び出しを行っていますが、以下のコードでは完了するまでに約 2 分かかります。誰かがプロセスをスピードアップするのを手伝ってくれますか?

private void button4_Click(object sender, EventArgs e)
    {
        WebRequest req;
        WebResponse res;
        string[] lines = File.ReadAllLines(@"c:\data\temp.txt");
        for (int i = 0; i < lines.Count(); i++)
        {
            req = WebRequest.Create(lines[i]); 
            res = req.GetResponse();
            StreamReader rd = new StreamReader(res.GetResponseStream(), Encoding.ASCII);
            rd.Close();
            res.Close();
            textBox1.Text += ".";
        }
    } 

どうもありがとう

4

3 に答える 3

0

ボトルネックはインターネット接続であるため、それほど高速化することはできません。ただし、できることがあります。

1) LINQ で行数をカウントしないでください。これは配列であり、そのサイズはわかっています (マイクロ最適化、この変更に気付くことはありません)。

2)using使い捨てオブジェクトを解放するために使用します(速度、より良いエラー処理とは関係ありません。コードに問題が発生した場合、GCでリソースを解放します)。

3) 平行にする。これにより、物事が少しスピードアップします。

private void button4_Click(object sender, EventArgs e)
{
    var lines = File.ReadAllLines(@"c:\data\temp.txt");

    var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
    Parallel.ForEach(lines, options, line => 
    {
        var request = WebRequest.Create(line);

        using (var response = request.GetResponse()) 
        {
            var reader = new StreamReader(response.GetResponseStream(), Encoding.ASCII);

            // Do your stuff

            BeginInvoke(new MethodInvoker(delegate 
            {
                textBox1.Text += ".";
            }));
        }
    });
} 

さらにいくつかのメモ:

  • MaxDegreeOfParallelism同時リクエストの最大数を設定します。複数のアクティブな同時接続は、物事を無期限に高速化するわけではなく、速度を低下させることさえあります。この値を妥当な値に設定するには、いくつかの試行が役立ちます。

  • エラーチェックはありませんが、ネットワークが一時的にうまくいかない場合がありますが、少し遅れて期待どおりに動作する可能性があります。System.Net.WebException: The remote name could not be resolve and this for I/O operationsも読むことをお勧めします。

より完全な例にするために、クリック イベント ハンドラーは次のようになります。

private void button4_Click(object sender, EventArgs e)
{
    var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
    Parallel.ForEach(ReadUrlList(@"c:\data\temp.txt"), options, ProcessUrl);
}

各 URL を処理し、URL リストを読み取る実際のコード:

private static string[] ReadUrlList(string path)
{
    return File.ReadAllLines(@"c:\data\temp.txt");
}

private void ProcessUrl(string url)
{
    ProcessResponse(response =>
    {
        using (var reader = new StreamReader(response.GetResponseStream(), Encoding.ASCII))
       {
            // Do your stuff

            // We're working on separate threads, to access UI we
            // have to dispatch the call to UI thread. Note that
            // code will be executed asynchronously then local
            // objects may have been disposed!
            BeginInvoke(new MethodInvoker(delegate 
            {
                textBox1.Text += ".";
            }));
        }
    });
} 

このヘルパー メソッドを使用して、ネットワーク操作の試行/待機パターンを非表示にします。

private static void ProcessResponse(string url, Action<WebResponse> action) 
{
    for (int i=1; i <= NumberOfRetries; ++i) 
    {
        try 
        {
            var request = WebRequest.Create(line);

            using (var response = request.GetResponse()) 
            {
                action(response);
            }

            break;
        }
        catch (Exception e) 
        {
            if (i == NumberOfRetries)
                throw;

            Thread.Sleep(DelayOnRetry);
        }
    }
}

private const int NumberOfRetries = 3;
private const int DelayOnRetry = 1000;
于 2015-06-29T09:40:10.097 に答える
0

これには Microsoft の Reactive Framework を使用することをお勧めします。NuGet "Rx-Main"、"Rx-WinForms"/"Rx-WPF"。

コードは次のようになります。

private void button4_Click(object sender, EventArgs e)
{
    var query =
        from line in File.ReadAllLines(@"c:\data\temp.txt").ToObservable()
        from result in Observable.Defer(() =>
        {
            var req = WebRequest.Create(line);
            return
                Observable.Using(
                    () => req.GetResponse(),
                    res => Observable.Using(
                        () => new StreamReader(res.GetResponseStream(), Encoding.ASCII),
                        st => Observable.Start(() => st.ReadToEnd())));
        })
        select new { line, result };

    query
        .ObserveOn(textBox1)
        .Subscribe(x => textBox1.Text += ".");
}

ストリームから文字列を読み取ろうとしていると仮定しました。

このコードは、すべての中間オブジェクトを適切に破棄します。また、リクエストを正しくマルチスレッド化し、結果を UI スレッドにマーシャリングして、テキスト ボックスのテキストを更新します。

このコードの少しきれいなバージョンは次のとおりです。

private void button4_Click(object sender, EventArgs e)
{
    var query =
        from line in File.ReadAllLines(@"c:\data\temp.txt").ToObservable()
        from result in Observable.Using(
            () => new WebClient(),
            wc => Observable.Start(() => wc.DownloadString(new Uri(line))))
        select new { line, result };

    query
        .ObserveOn(textBox1)
        .Subscribe(x => textBox1.Text += ".");
}

ダウンロードに使用WebClientします。必要に応じてまだマルチスレッドです。

于 2015-06-29T12:24:03.787 に答える