2017-09-26 20 views
1

まず最初に、私は同様の質問を読んでおり、一貫した説明をしません。私はBlockingCollection<WebClient> ClientQueueを使ってWebクライアントを提供しています。私は彼らに処理関数を与え、非同期スクレイピングを開始します。WebClientは同時I/O操作をサポートしていません - DownloadStringAsync - スクレイピング

// Create queue of WebClient instances 
BlockingCollection<WebClient> ClientQueue = new BlockingCollection<WebClient>(); 
for (int i = 0; i < 10; i++) 
{ 
    ClientQueue.Add(new WebClient()); 
} 

//Triggering Async Calls 
foreach (var item in source) 
{ 
    var worker = ClientQueue.Take(); 
    worker.DownloadStringCompleted += (sender, e) => HandleJson(sender, e, ClientQueue, item); 
    worker.DownloadStringAsync(uri); 
} 

public static void HandleJson(object sender, EventArgs e, BlockingCollection<WebClient> ClientQueue, string item) 
{ 
    var res = (DownloadStringCompletedEventArgs) e; 
    var jsonData = res.Result; 
    var worker = (WebClient) sender; 
    var root = JsonConvert.DeserializeObject<RootObject>(jsonData); 
    // Record the data 
    while (worker.IsBusy) Thread.Sleep(5); // wait for the webClient to be free 
    ClientQueue.Add(worker); 
} 

私は、このエラーメッセージが出ます:

Webクライアントが同時I/O操作をサポートしていませんが。

他のスレッド:

  • ここでは、問題がWebClient.IsBusy = falseまで待つことであることを示唆しているが、私はキューにWebクライアントをバックputing前にこれをやって答えます。私はそれが新しいWebクライアントをインスタンス化することを提案ここで https://stackoverflow.com/a/7474959/2132352

    にクライアントがIsBusy=false https://stackoverflow.com/a/9765812/7111121

  • 自体を行った後、新しい要求を実行できない理由はここにそれがプロセスを最適化するために、リサイクルwebclientsを使用することを示唆して

  • を理解していません(もちろん簡単な解決策ですが、私はオブジェクトが使用する方法を隠す何かを望まない)。それはまた、操作をキャンセルすることを示唆していますが、これは助けにはなりません。

+0

はどのようにあなたは私が見るに質問を編集した、[OK]キュー – Bigsby

+0

を充填している私たちを表示しますより多くのコード – aam

答えて

1

問題は、特定のWebクライアントがキューから取られるたびに、前のイベントハンドラを登録解除せずにworker.DownloadStringCompletedイベントに新しいイベントハンドラを登録することである - ので、イベントハンドラを計上する。結果として、非同期ダウンロードの完了後にHandleJsonが複数回呼び出されるため、ClientQueue.Add(worker)は同じクライアントを複数回キューに返します。同じWebClientで2回の同時ダウンロードが行われるまでには時間がかかります。

これは、WebClientの作成中にイベントハンドラを1回だけ登録し、HandleJsonメソッドからパラメータを削除することで簡単に修正できます。

BlockingCollection<WebClient> ClientQueue = new BlockingCollection<WebClient>(); 
for (int i = 0; i < 2; i++) 
{ 
    var worker = new WebClient(); 
    worker.DownloadStringCompleted += (sender, e) => HandleJson(sender, e, ClientQueue); 
    ClientQueue.Add(worker); 
} 

パラメータitemが必要な場合は、DownloadStringAsync(uri, item)にパラメータとして渡すとres.UserStateからそれを読むには:

foreach (var item in source) 
{ 
    var worker = ClientQueue.Take(); 
    worker.DownloadStringAsync(uri, item); 
} 

public static void HandleJson(object sender, DownloadStringCompletedEventArgs e, BlockingCollection<WebClient> ClientQueue) 
{ 
    string item = (string)res.UserState; 
    ... 
} 
+0

ありがとうございます。完璧な答え。今明らかにされている以上のものです。 while(worker.IsBusy)Thread.Sleep(5);よりももっとエレガントなものの提案がありますか? ? – aam

+1

少なくとも 'WebClient'の[参照実装](https:// github)では、' DownloadStringCompleted'イベントハンドラが呼び出される前に常に 'false'になるため、' IsBusy'のチェックは完全に無意味です.com/Microsoft/referencesource/blob/4fe4349175f4c5091d972a7e56ea12012f1e7170 /システム/ネット/システム/ネット/ webclient.cs#L1763 –

+0

ありがとうございました – aam

関連する問題