プール スレッドを使用する際の問題は、ほとんどの時間を Web サイトからの応答を待つことに費やすことです。使用の問題Parallel.ForEach
は、並列処理が制限されることです。
非同期 Web 要求を使用することで、最高のパフォーマンスが得られました。Semaphore
同時リクエストの数を制限するために a を使用し、コールバック関数がスクレイピングを行いました。
メイン スレッドはSemaphore
、次のようにを作成します。
Semaphore _requestsSemaphore = new Semaphore(20, 20);
は20
、試行錯誤によって導き出されました。制限要因は DNS 解決であり、平均して約 50 ミリ秒かかることが判明しました。少なくとも、私の環境ではそうでした。20 の同時リクエストが絶対最大数でした。15がおそらくより合理的です。
メイン スレッドは基本的に次のようにループします。
while (true)
{
_requestsSemaphore.WaitOne();
string urlToCrawl = DequeueUrl(); // however you do that
var request = (HttpWebRequest)WebRequest.Create(urlToCrawl);
// set request properties as appropriate
// and then do an asynchronous request
request.BeginGetResponse(ResponseCallback, request);
}
プール スレッドで呼び出されるResponseCallback
メソッドは、処理を実行し、応答を破棄してから、別の要求を行うことができるようにセマフォを解放します。
void ResponseCallback(IAsyncResult ir)
{
try
{
var request = (HttpWebRequest)ir.AsyncState;
// you'll want exception handling here
using (var response = (HttpWebResponse)request.EndGetResponse(ir))
{
// process the response here.
}
}
finally
{
// release the semaphore so that another request can be made
_requestSemaphore.Release();
}
}
前述したように、制限要因は DNS 解決です。DNS 解決は呼び出しスレッド (この場合はメイン スレッド) で行われることがわかります。これは本当に非同期ですか?を参照してください。詳細については。
これは実装が簡単で、非常にうまく機能します。20 を超える同時リクエストを取得することも可能ですが、私の経験では、そうするにはかなりの労力が必要です。大量の DNS キャッシングを行う必要がありましたが、それは困難でした。
Task
おそらく、C# 5.0 (.NET 4.5) の新しい非同期機能を使用して、上記を単純化できます。しかし、私はそれらについて十分に詳しく知りません。