我有一个应用程序,我需要一次制作很多httpWebRequest,并尽可能快地获得每个响应。 Actualy我不需要快速获得所有响应,我需要尽可能快地得到每个响应。我的意思是,我发送每个请求之间必须有最短的时间。
我阅读了很多关于async / await技术的文章并选择了这个解决方案:
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;
}
所以我的问题是:为什么这么大的差异?我认为当我使用等待I / O操作时 - 这是一种好方法,它是执行HTTP请求的最佳方式。但在实践中并不完全正确。
在没有async / await的解决方案中,我们在等待响应时有线程阻塞,如果它不是Google,并且将是一个长时间运行的服务器,这个请求将阻止我的所有应用。
可能有更好的方法来解决我的问题吗?
P.S .:
这个“神奇”:
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
只是来自互联网的一些建议,我不知道它是如何工作的。
更新 感谢Servy,我最终理解了我在测量中的错误。现在Process方法如下:
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毫秒)。但无论如何它比async / await更快。 async / await方法必须更慢吗?
答案 0 :(得分:1)
使用秒表来衡量整个操作的时间,你会发现时间会更加接近。
基本上,您可以更快地在异步版本中启动秒表,这就是他们出现这么长时间的原因。
在异步解决方案中,您可以同步启动每个操作,然后异步等待它们全部完成。在这种情况下,当应用程序启动时,秒表几乎都会立即启动。
对于您的同步解决方案,您甚至不会启动秒表,直到每个任务都被安排在线程池线程中运行,被分配给线程,并且实际上是从该线程运行的。很多等待都将在这里发生,你还没有开始你的秒表。