【问题标题】:C# Running many async tasks the same timeC# 同时运行多个异步任务
【发布时间】:2017-09-23 13:43:04
【问题描述】:

我对异步任务有点陌生。

我有一个函数,它可以获取学生 ID 并从具有所需 ID 的特定大学网站上抓取数据。

    private static HttpClient client = new HttpClient();
    public static async Task<Student> ParseAsync(string departmentLink, int id, CancellationToken ct)
    {
        string website = string.Format(departmentLink, id);
        try
        {
            string data;
            var stream = await client.GetAsync(website, ct);
            using (var reader = new StreamReader(await stream.Content.ReadAsStreamAsync(), Encoding.GetEncoding("windows-1256")))
                data = reader.ReadToEnd();

            //Parse data here and return Student.
        } catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

而且它工作正常。有时虽然我需要为很多学生运行此功能,所以我使用以下

        for(int i = ids.first; i <= ids.last; i++)
        {
            tasks[i - ids.first] = ParseStudentData.ParseAsync(entity.Link, i, cts.Token).ContinueWith(t =>
            {
                Dispatcher.Invoke(() =>
                {
                    listview_students.Items.Add(t.Result);
                    //Students.Add(t.Result);
                    //lbl_count.Content = $"{listview_students.Items.Count}/{testerino.Length}";
                });
            });
        }

我将任务存储在一个数组中,以便稍后等待。

只要学生人数在(0,~600?)之间,这也可以很好地工作,这有点随机。 然后对于尚未解析的所有其他学生抛出任务被取消

请记住,我从不使用取消令牌。

我需要在这么多学生身上运行这个功能,它总共可以达到大约 9000 个异步任务。那么发生了什么?

【问题讨论】:

    标签: c# parsing asynchronous task httpclient


    【解决方案1】:

    当您在如此短的时间内排队 9000 个请求时,您基本上是在对网站进行拒绝服务攻击。这不仅会导致您出错,而且可能会关闭网站。最好将并发请求的数量限制在一个更合理的值(比如 30)。虽然可能有几种方法可以做到这一点,但我想到的一种方法如下:

    private async Task Test()
    {
      var tasks = new List<Task>();
      for (int i = ids.first; i <= ids.last; i++)
      {
        tasks.Add(/* Do stuff */);
        await WaitList(tasks, 30);
      }
    }
    
    private async Task WaitList(IList<Task> tasks, int maxSize)
    {
      while (tasks.Count > maxSize)
      {
        var completed = await Task.WhenAny(tasks).ConfigureAwait(false);
        tasks.Remove(completed);
      }
    }
    

    其他方法可能会利用 .Net 类(例如 BlockingCollection)利用生产者/消费者模式

    【讨论】:

      【解决方案2】:

      这是我根据@erdomke 代码得出的结果:

          public static async Task ForEachParallel<T>(
            this IEnumerable<T> list, 
            Func<T, Task> action, 
            int dop)
          {
              var tasks = new List<Task>(dop);
              foreach (var item in list)
              {
                  tasks.Add(action(item));
      
                  while (tasks.Count >= dop)
                  {
                      var completed = await Task.WhenAny(tasks).ConfigureAwait(false);
                      tasks.Remove(completed);
                  }
              }
      
              // Wait for all remaining tasks.
              await Task.WhenAll(tasks).ConfigureAwait(false);
          }
      
          // usage
          await Enumerable
              .Range(1, 500)
              .ForEachParallel(i => ProcessItem(i), Environment.ProcessorCount);
      

      【讨论】:

        猜你喜欢
        • 2011-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多