2017-06-13 6 views
4

現在、私は大量のWeb APIリクエストを作成中です。私はこのプロセスをasyncにしようとしましたが、妥当な時間内にこれを行うことができますが、接続を絞り込むことができないため、10リクエスト/秒を送信しません。私は調整のためのセマフォを使用していますが、私はネストされたループを持っているので、この文脈でどのように動作するのか完全にはわかりません。ループで同時に非同期リクエストを抑制する

私は基本的にモデルのリストを取得しており、各モデルにはその中の日のリストがあります。私はモデルの中で毎日のリクエストをする必要があります。日数は、1から約50まで、99%の間であればいつでも1になるはずです。だからasyncそれぞれのモデルには約3000があるので、私はasyncが完了する必要がある複数の日がある場合の日にしたいと思います。私は10リクエスト/秒以下で滞在する必要があるので、これを行う最善の方法は、操作全体でリクエスト制限を10に設定することだと思いました。チェーン全体に接続を制限するセマフォを置くことができる場所はありますか?

個々のリクエストごとに、2個の異なるデータを2回要求する必要があり、このAPIは現在バッチ処理をサポートしていません。

私はC#に新しく、asyncに非常に新しいとWebRequests/HttpClientに非常に新しいので、任意の助けに感謝します。ここにすべての関連コードを追加しようとしました。他に何かが必要なら私に知らせてください。

public static async Task GetWeatherDataAsync(List<Model> models) 
{ 
    SemaphoreSlim semaphore = new SemaphoreSlim(10); 
    var taskList = new List<Task<ComparisonModel>>(); 

    foreach (var x in models) 
    { 
     await semaphore.WaitAsync(); 
     taskList.Add(CompDaysAsync(x)); 
    } 

    try 
    { 
     await Task.WhenAll(taskList.ToArray()); 
    } 
    catch (Exception e) { } 
    finally 
    { 
     semaphore.Release(); 
    } 
} 

public static async Task<Models> CompDaysAsync(Model model) 
{ 
    var httpClient = new HttpClient(); 
    httpClient.DefaultRequestHeaders.Authorization = new 
       Headers.AuthenticationHeaderValue("Token","xxxxxxxx"); 
    httpClient.Timeout = TimeSpan.FromMinutes(5); 
    var taskList = new List<Task<Models.DateTemp>>(); 

    foreach (var item in model.list) 
    { 
     taskList.Add(WeatherAPI.GetResponseForDayAsync(item, 
      httpClient, Latitude, Longitude)); 
    } 
    httpClient.Dispose(); 
    try 
    { 
     await Task.WhenAll(taskList.ToArray()); 
    } 
    catch (Exception e) { } 

    return model; 
} 

public static async Task<DateTemp> GetResponseForDayAsync(DateTemp date, HttpClient httpClient, decimal? Latitude, decimal? Longitude) 
{ 
    var response = await httpClient.GetStreamAsync(request1); 
    StreamReader myStreamReader = new StreamReader(response); 
    string responseData = myStreamReader.ReadToEnd(); 
    double[] data = new double[2]; 
    if (responseData != "[[null, null]]") 
    { 
     data = Array.ConvertAll(responseData.Replace("[", "").Replace("]", "").Split(','), double.Parse); 
    } 
    else { data = null; }; 

    double precipData = 0; 
    var response2 = await httpClient.GetStreamAsync(request2); 
    StreamReader myStreamReader2 = new StreamReader(response2); 
    string responseData2 = myStreamReader2.ReadToEnd(); 
    if (responseData2 != null && responseData2 != "[null]" && responseData2 != "[0.0]") 
    { 
     precipData = double.Parse(responseData2.Replace("[", "").Replace("]", "")); 
    } 
    date.Precip = precipData; 

    if (data != null) 
    { 
     date.minTemp = data[0]; 
     date.maxTemp = data[1]; 
    } 
    return date; 
} 
+0

私は単純に 'Parallel.ForEach'を使う前にこれをしました。 'ParallelOptions'を必要とするオーバーロードは' MaxDegreeOfParallelism'を設定しますが、最初に各モデルの日を 'Enumerable.SelectMany'で平坦化する必要があります。 – Biscuits

+0

私のコレクションをSelectManyすれば、モデル自体と関係のない私のすべての日々の大きなリストが得られます。それが本質的に関係しているのか、それを確実にするために何か特別なことをする必要がありますか? – DevDevDev

+0

親オブジェクトと要素の両方からの情報を新しいオブジェクトに投影する結果セレクタを指定できる 'SelectMany'のオーバーロードがあります。 'Linq'構文は、それを簡単にします。 「Parallel.ForEach」は、アクション(またはタスク)を非同期で実行し、各繰り返しで完了するように依頼できることを覚えておいてください。 – Biscuits

答えて

1

SemaphoreSlimが何をしているのか完全に分からないと思います。

  1. あなたのセマフォはそうすべてのGetWeatherDataAsyncメソッドの呼び出しは、他のクライアントを待たずに、あなたのAPIに10呼び出しを起動し、メソッドレベルベースのローカル変数です。
  2. また、あなたのコードを使用すると、各反復でセマフォを待っているので、これらの要求が積層されている、models.Count > 10場合、デッドロックし、あなたがセマフォ解放していないよう11thのためにあなたのスレッドは、永遠にハングします:

    var semaphore = new SemaphoreSlim(10); 
    
    foreach (var item in Enumerable.Range(0, 15)) 
    { 
        // will stop after 9 
        await semaphore.WaitAsync(); 
        Console.WriteLine(item); 
    } 
    
    あなたが本当に行う必要がある何

インスタンスレベルにセマフォを移動(あるいはstaticキーワードとレベルを入力します)であり、それのためにGetWeatherDataAsync内部を待って、そしてfinallyブロックでReleaseを置きます。 Parallel.Foreachについては

- それはasync方法(それはasync/await前に導入された)を認識していないだ、と彼らはCPUバインドされているようなあなたの方法を見ていないとして、あなたは、このシナリオでは、それを使用しないでください。

+0

'Parallel.ForEach'の使用についてのあなたの指摘は誤っています。どのようにフレームワークライブラリはC#言語機能に依存するのですか? – Biscuits

+0

私は、非同期ラムダを使用していないので、Parallel Foreachが非同期メソッドでうまく動作せず、最初に返された後に完了したメソッドを単にマークすることを意味します。 – VMAtm

+1

ああ、私はあなたが意味するものを参照してください。だから、あなたはすべての反復の中で「待つ」ことができません。 – Biscuits

関連する問題