这些网络请求实际上是并发的吗?

Are these webrequests actually concurrent?

我有一个只有 4 个 URL 的 UrlList,我想用它来发出 4 个并发请求。下面的代码是否真的发出了同时启动的 4 个请求?

我的测试似乎表明确实如此,但我是否正确地认为实际上会有 4 个请求同时从 URL 目标检索数据,或者它只是以这种方式出现?

    static void Main(string[] args)
    {
        var t = Do_TaskWhenAll();
        t.Wait();
    }

    public static async Task Do_TaskWhenAll()
    {
        var downloadTasksQuery = from url in UrlList select Run(url);

        var downloadTasks = downloadTasksQuery.ToArray();

        Results = await Task.WhenAll(downloadTasks);
    }

    public static async Task<string> Run(string url)
    {
        var client = new WebClient();
        AddHeaders(client);

        var content = await client.DownloadStringTaskAsync(new Uri(url));

        return content;
    }

正确,当调用 ToArray 时,可枚举 downloadTasksQuery 将为每个 URL、运行 并发的 Web 请求生成一个任务。

await Task.WhenAll 确保您的任务仅在所有 Web 请求都已完成时完成。

您可以重写代码以减少冗长,同时有效地执行相同的操作,如下所示:

public static async Task Do_TaskWhenAll()
{
    var downloadTasks = from url in UrlList select Run(url);

    Results = await Task.WhenAll(downloadTasks);
}

不需要 ToArray 因为 Task.WhenAll 会为你枚举你的枚举。

我建议您使用 HttpClient 而不是 WebClient。使用 HttpClient,您不必为每个并发请求创建一个新的客户端实例,因为它允许您重复使用同一个客户端同时执行多个请求。

不,不能保证您的请求会并行或立即执行。

Starting a task merely queues it to the thread pool. 如果池中的所有线程都被占用,则该任务必须等待直到线程释放。

在您的情况下,由于池中有相对较多的线程可用,而您只排队少量项目,因此池在处理它们时没有问题。您的任务越多立即排队,这越有可能改变。

如果你真的需要并发,你需要知道线程池的大小是多少,它有多忙。 The ThreadPool class will help you to manage this.

简短的回答是肯定的:如果您生成多个任务而不单独等待每个任务,只要它们真正异步,它们就可以同时 运行。

等待 DownloadStringTaskAsync 时,从您的 Run 方法返回 Task,允许在等待响应时进行下一次迭代。

因此允许发送下一个 HTTP 请求,而无需等待第一个请求完成。

顺便说一句,你的方法可以写得更简洁:

public static async Task Do_TaskWhenAll()
{
    Results = await Task.WhenAll(UrlList.Select(Run));
}

Task.WhenAll 有一个重载接受从 UrlList.Select(Run).

返回的 IEnumerable<Task<TResult>>