async HttpWebRequest vs sync in multithreaded app
本文关键字:multithreaded app in sync HttpWebRequest vs async | 更新日期: 2023-09-27 18:33:30
我有一个应用程序,我需要一次制作很多httpWebRequest,并尽快获得每个应用程序的响应。实际上,我不需要快速获得所有响应,我需要尽可能快地获得每个响应。我的意思是,在我发送每个请求和获取响应之间必须有最短的时间。
我阅读了许多关于异步/等待技术的文章,并选择了这个解决方案:
private static void Main()
{
ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
ProcessAsync("https://www.google.ru");
Console.ReadKey();
}
private static void ProcessAsync(string url)
{
for (var i = 1; i <= 100; i++)
Task.Factory.StartNew(async () => await ProcessInternalAsync(url));
}
private static async Task ProcessInternalAsync(string url)
{
var request = GetRequest(url);
var sw = Stopwatch.StartNew();
await GetStringContentAsync(request);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public static async Task<string> GetStringContentAsync(HttpWebRequest webRequest)
{
using (var response = (HttpWebResponse) await webRequest.GetResponseAsync()
.ConfigureAwait(false))
{
var content = await GetStringContentFromResponseAsync(response)
.ConfigureAwait(false);
return content;
}
}
private static async Task<string> GetStringContentFromResponseAsync(HttpWebResponse response)
{
using (var responseStream = GetStreamForResponse(response))
{
if (responseStream == null)
return null;
using (var streamReader = new StreamReader(responseStream))
{
var content = await streamReader.ReadToEndAsync()
.ConfigureAwait(false);
return content;
}
}
}
这行得通,但酿酒厂很慢。(每个请求 500-1600 毫秒(
所以我尝试了没有异步/等待的"简单"解决方案:
private static void Main()
{
ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
Process("https://www.google.ru");
Console.ReadKey();
}
private static void Process(string url)
{
for (var i = 1; i <= 100; i++)
Task.Factory.StartNew(() => ProcessInternal(url));
}
private static void ProcessInternal(string url)
{
var request = GetRequest(url);
var sw = Stopwatch.StartNew();
GetStringContent(request);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public static string GetStringContent(HttpWebRequest webRequest)
{
using (var response = (HttpWebResponse) webRequest.GetResponse())
{
var content = GetStringContentFromResponse(response);
return content;
}
}
private static string GetStringContentFromResponse(HttpWebResponse response)
{
using (var responseStream = GetStreamForResponse(response))
{
if (responseStream == null)
return null;
using (var streamReader = new StreamReader(responseStream))
{
var content = streamReader.ReadToEnd();
return content;
}
}
}
而且它的工作速度要快得多!(每个请求 55-180 毫秒(。
以下是一些其他方法:
private static HttpWebRequest GetRequest(string url)
{
var request = (HttpWebRequest) WebRequest.Create(url);
request.Timeout = 15000;
request.Proxy = null;
request.Headers.Add("Accept-Encoding", "gzip,deflate");
return request;
}
private static Stream GetStreamForResponse(HttpWebResponse webResponse)
{
var responseStream = webResponse.GetResponseStream();
if (responseStream == null)
return null;
Stream stream;
switch (webResponse.ContentEncoding.ToUpperInvariant())
{
case "GZIP":
stream = new GZipStream(responseStream, CompressionMode.Decompress);
break;
case "DEFLATE":
stream = new DeflateStream(responseStream, CompressionMode.Decompress);
break;
default:
stream = responseStream;
break;
}
return stream;
}
所以我的问题是:为什么差异这么大?我认为当我使用 await 与 I/O 操作时 - 这是一个很好的方法,也是执行 HTTP 请求的最佳方式。但实际上并不完全正确。
在没有 async/await 的解决方案中,我们在等待响应时有线程阻塞,如果它不是 Google 并且将是具有长时间运行操作的服务器,则此请求将阻止我的所有应用程序。
也许有更好的方法来解决我的问题?
附言:这个"魔力": ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
只是来自互联网的一些建议,我不知道它是如何工作的。
更新多亏了塞尔维,我终于明白了我在测量方面的错误。现在处理方法如下所示:
private static void Process(string url)
{
var taskCollection = new List<Task>();
for (var i = 1; i <= 100; i++)
{
var sw = Stopwatch.StartNew();
taskCollection.Add(Task.Factory.StartNew(() =>
{
ProcessInternal(url);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}));
}
Task.WaitAll(taskCollection.ToArray());
}
现在第二个解决方案显示其毫秒(请求为 300-1000 毫秒(。但无论如何,它比异步/等待更快。异步/等待方法必须更慢吗?
用秒表测量整个操作的时间,你会发现时间会更近。
基本上,您更快地在异步版本中启动秒表,这就是为什么它们似乎需要更长的时间。
在异步解决方案中,同步启动每个操作,然后异步等待所有操作完成。 在这种情况下,当应用程序启动时,秒表几乎都会立即启动。
对于同步解决方案,您甚至不会启动秒表,直到每个任务被安排为在线程池线程中运行,分配给线程,并且实际上从该线程运行。 大部分等待将在这里发生,您甚至还没有启动秒表。