2016-12-12 30 views
0

我想通過一個Web服務處理50,000個URL的列表,該服務的提供者允許每秒5個連接。併發和異步處理大量任務

我需要在遵守供應商規則的同時處理這些網址。

這是我當前的代碼:

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();我將創建目標Web服務的一個新的對象,但是這僅僅是爲了讓代碼通用。

這是處理和處理大量連接列表的正確方法嗎?並且無論如何,我可以將每秒建立的連接數限制爲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操作,然後async/await的方法是最有效的,因爲只使用一個線程和進程時,以前的任務等待來自Web服務或文件響應其他任務它編寫IO操作。

+0

'Task.Delay(5000)'將確保任務在5秒後完成?我需要的是確保只有5個任務在1秒內運行。是的,計算是另一個異步任務,將輸出寫入文本文件。 – PyQL

+0

'Task.Delay(1000)'被添加到任務集合,然後'Task.WhenAll'確保接下來的5個URL將至少在1秒後處理或者所有5個URL已經處理。 – Fabio

+0

感謝您的修改,那麼什麼是'SplitToGroupsOfFive' – PyQL