1 c# parsing asynchronous httpclient task
我对异步任务有点陌生。
我有一个函数,可以获取学生 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 个异步任务。那么发生了什么事?
当您在如此短的时间内排队 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