这些网络请求实际上是并发的吗?
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>>
我有一个只有 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>>