【问题标题】:Asynchronous parallel web requests in ASP.NET MVC(C#)ASP.NET MVC(C#) 中的异步并行 Web 请求
【发布时间】:2020-04-17 09:28:04
【问题描述】:

我有一个小项目,需要使用 C# 下载多个网站的 html 文档并使其尽可能快地执行。在我的场景中,我可能需要根据某些条件使用代理来切换 IP。我想利用 C# Asynchronous Tasks 让它执行尽可能多的请求,以使整个过程快速高效。

这是我目前的代码。

public class HTMLDownloader
{

    public static List<string> URL_LIST = new List<string>();
    public static List<string> HTML_DOCUMENTS = new List<string>();

    public static void Main()
    {
        for (var i = 0; i < URL_LIST.Count; i++)
        {
            var html = Task.Run(() => Run(URL_LIST[i]));
            HTML_DOCUMENTS.Add(html.Result);
        }
    }

    public static async Task<string> Run(string url)
    {
        var client = new WebClient();
        //Handle Proxy Credentials client.Proxy= new WebProxy();
        string html = "";
        try
        {
            html = await client.DownloadStringTaskAsync(new Uri(url));
            //if(condition ==true)
            //{
            //  return html;
            //}
            //else
            //{
            //  Switch IP and try again
            //}
        }
        catch (Exception e)
        {

        }

        return html;
    }

这里的问题是我并没有真正利用发送多个网络请求,因为每个请求都必须完成才能开始下一个请求。有更好的方法吗?例如,一次发送 10 个 Web 请求,然后在其中一个请求完成时发送一个新请求。

谢谢

【问题讨论】:

  • 您确定您的问题与 ASP.NET 相关吗?

标签: c# .net asynchronous async-await httpwebrequest


【解决方案1】:

我想利用 C# Asynchronous Tasks 让它执行尽可能多的请求,以使整个过程快速高效。

您可以使用Task.WhenAll 来获得异步并发。

例如,一次发送 10 个 Web 请求,然后在其中一个请求完成时发送一个新请求。

限制异步并发,请使用SemaphoreSlim

public static async Task Main()
{
  using var limit = new SemaphoreSlim(10); // 10 at a time
  var tasks = URL_LIST.Select(Process).ToList();
  var results = await Task.WhenAll(tasks);
  HTML_DOCUMENTS.AddRange(results);

  async Task<string> Process(string url)
  {
    await limit.WaitAsync();
    try { return await Run(url); }
    finally { limit.Release(); }
  }
}

【讨论】:

    【解决方案2】:

    一种方法是使用Task.WhenAll

    创建一个将在所有提供的任务都完成时完成的任务 完成。

    前提是,Select将所有任务放到一个List,awaittasklist中,Task.WhenAll,Select结果

    public static async Task Main()
    {
       var tasks = URL_LIST.Select(Run); 
       await Task.WhenAll(tasks);
       var results = tasks.Select(x => x.Result);
    }
    

    注意:WhenAll 的结果也是结果的集合

    【讨论】:

    • 很好,但最后的Select 是不必要的步骤。
    【解决方案3】:

    首先将您的 Main 更改为异步。

    然后您可以使用 LINQ Select 并行运行 Tasks

    public static async Task Main()
    {
        var tasks = URL_LIST.Select(Run);
    
        string[] documents = await Task.WhenAll(tasks);
    
        HTML_DOCUMENTS.AddRange(documents);
    }
    

    一旦所有任务完成,Task.WhenAll 会将Task 结果解包到一个数组中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-24
      • 1970-01-01
      • 2010-10-16
      • 1970-01-01
      相关资源
      最近更新 更多