2016-12-12 25 views
0

Webサービスを通じて50,000のURLのリストを処理したいと考えています。このサービスのプロバイダは1秒あたり5つの接続を許可しています。多数のタスクを同時に非同期に処理する

これらのURLをプロバイダの規則に準拠して処理する必要があります。

これは私の現在のコードです:

static void Main(string[] args) 
{ 
    process_urls().GetAwaiter().GetResult(); 

} 
public static async Task process_urls() 
{ 
    // let's say there is a list of 50,000+ URLs 
    var urls = System.IO.File.ReadAllLines("urls.txt"); 

    var allTasks = new List<Task>(); 
    var throttler = new SemaphoreSlim(initialCount: 5); 

    foreach (var url in urls) 
    { 
     await throttler.WaitAsync(); 

     allTasks.Add(
      Task.Run(async() => 
      { 
       try 
       { 
        Console.WriteLine(String.Format("Starting {0}", url)); 
        var client = new HttpClient(); 
        var xml = await client.GetStringAsync(url); 
        //do some processing on xml output 
        client.Dispose(); 
       } 
       finally 
       { 
        throttler.Release(); 
       } 
      })); 
    } 
    await Task.WhenAll(allTasks); 
} 

代わりのvar client = new HttpClient(); Iは、対象のWebサービスの新しいオブジェクトを作成しますが、これは単にコードをジェネリックにすることです。

巨大な接続リストを処理して処理する正しい方法ですか?とにかく、現在の実装では時間枠を考慮しないため、1秒あたりの確立された接続数を5に制限できますか? Webサービスからの

おかげ

+0

Parallel.ForEachループを使用し、並列度を[ここ](http://stackoverflow.com/a/9290531/6170142)に示すように制限できます。 –

答えて

2

読書値はマルチスレッディングなしで非同期に行うことができるIO操作です。
スレッドは何もしません。この場合、応答を待っているだけです。したがって、並列を使用することは単にリソースを無駄にすることに過ぎません。

public static async Task process_urls() 
{ 
    var urls = System.IO.File.ReadAllLines("urls.txt"); 

    var allTasks = new List<Task>(); 
    var throttler = new SemaphoreSlim(initialCount: 5); 

    foreach (var urlGroup in SplitToGroupsOfFive(urls)) 
    { 
     var tasks = new List<Task>(); 
     foreach(var url in urlGroup) 
     { 
      var task = ProcessUrl(url); 
      tasks.Add(task); 
     } 
     // This delay will sure that next 5 urls will be used only after 1 seconds 
     tasks.Add(Task.Delay(1000)); 

     await Task.WhenAll(tasks.ToArray()); 
    } 
} 

private async Task ProcessUrl(string url) 
{ 
    using (var client = new HttpClient()) 
    { 
     var xml = await client.GetStringAsync(url); 
     //do some processing on xml output 
    } 
} 

private IEnumerable<IEnumerable<string>> SplitToGroupsOfFive(IEnumerable<string> urls) 
{ 
    var const GROUP_SIZE = 5; 
    var string[] group = null; 
    var int count = 0; 

    foreach (var url in urls) 
    { 
     if (group == null) 
      group = new string[GROUP_SIZE]; 

     group[count] = url; 
     count++; 

     if (count < GROUP_SIZE) 
      continue; 

     yield return group; 

     group = null; 
     count = 0; 
    } 

    if (group != null && group.Length > 0) 
    { 
     yield return group.Take(group.Length); 
    } 
} 

あなたはレスポンスの「処理」はIO操作でもあることを言及ので、それが唯一のスレッドやプロセス、以前のタスクは、Webサービスからまたはファイルからの応答を待っている他のタスクを使用しているため、その後、async/awaitアプローチは、最も効率的ですIO操作を記述する。

+0

'Task.Delay(5000)'は5秒後にタスクが完了することを確認します。私が必要とするのは、わずか5つのタスクが1秒で実行されることを確認することです。そして、はい、計算は、出力をテキストファイルに書き込む別の非同期タスクです。 – PyQL

+0

タスクのコレクションに 'Task.Delay(1000)'が追加されました。その後、 'Task.WhenAll'は少なくとも1秒後に5つのURLが処理されるか、5つのURLがすべて処理されたことを確認します。 – Fabio

+0

修正をありがとう、 'SplitToGroupsOfFive'とは何か – PyQL

関連する問題