Warum blockiert die Stornierung so lange, wenn viele HTTP-Anfragen storniert werden?
Ich habe einen Code, der eine Stapelverarbeitung von HTML-Seiten mit Inhalten von einem bestimmten Host durchführt. Es wird versucht, mithilfe von eine große Anzahl (~ 400) gleichzeitiger HTTP-Anforderungen zu erstellenHttpClient
. Ich glaube, dass die maximale Anzahl gleichzeitiger Verbindungen durch begrenzt istServicePointManager.DefaultConnectionLimit
Daher wende ich keine eigenen Nebenläufigkeitsbeschränkungen an.
Nach dem Senden aller Anfragen asynchron anHttpClient
mitTask.WhenAll
kann der gesamte Batch-Vorgang mit abgebrochen werdenCancellationTokenSource
undCancellationToken
. Der Fortschritt des Vorgangs kann über eine Benutzeroberfläche angezeigt werden, und auf eine Schaltfläche kann geklickt werden, um den Vorgang abzubrechen.
Der Anruf nachCancellationTokenSource.Cancel()
blockiert für ca. 5 - 30 Sekunden. Dadurch friert die Benutzeroberfläche ein. Ist der Verdacht, dass dies auftritt, weil die Methode den Code aufruft, der für die Stornierungsbenachrichtigung registriert wurde.
HttpClient
scheint sich schon überschüssige Anfragen in die Warteschlange zu stellen.Durchführen derCancellationTokenSource.Cancel()
Methodenaufruf in einem Nicht-UI-Thread. Das hat nicht so gut funktioniert; Die Aufgabe lief nicht wirklich, bis die meisten anderen fertig waren. Ich denke einasync
Die Version der Methode würde gut funktionieren, aber ich konnte keine finden. Außerdem habe ich den Eindruck, dass es geeignet ist, die Methode in einem UI-Thread zu verwenden.DemonstrationCodeclass Program
{
private const int desiredNumberOfConnections = 418;
static void Main(string[] args)
{
ManyHttpRequestsTest().Wait();
Console.WriteLine("Finished.");
Console.ReadKey();
}
private static async Task ManyHttpRequestsTest()
{
using (var client = new HttpClient())
using (var cancellationTokenSource = new CancellationTokenSource())
{
var requestsCompleted = 0;
using (var allRequestsStarted = new CountdownEvent(desiredNumberOfConnections))
{
Action reportRequestStarted = () => allRequestsStarted.Signal();
Action reportRequestCompleted = () => Interlocked.Increment(ref requestsCompleted);
Func<int, Task> getHttpResponse = index => GetHttpResponse(client, cancellationTokenSource.Token, reportRequestStarted, reportRequestCompleted);
var httpRequestTasks = Enumerable.Range(0, desiredNumberOfConnections).Select(getHttpResponse);
Console.WriteLine("HTTP requests batch being initiated");
var httpRequestsTask = Task.WhenAll(httpRequestTasks);
Console.WriteLine("Starting {0} requests (simultaneous connection limit of {1})", desiredNumberOfConnections, ServicePointManager.DefaultConnectionLimit);
allRequestsStarted.Wait();
Cancel(cancellationTokenSource);
await WaitForRequestsToFinish(httpRequestsTask);
}
Console.WriteLine("{0} HTTP requests were completed", requestsCompleted);
}
}
private static void Cancel(CancellationTokenSource cancellationTokenSource)
{
Console.Write("Cancelling...");
var stopwatch = Stopwatch.StartNew();
cancellationTokenSource.Cancel();
stopwatch.Stop();
Console.WriteLine("took {0} seconds", stopwatch.Elapsed.TotalSeconds);
}
private static async Task WaitForRequestsToFinish(Task httpRequestsTask)
{
Console.WriteLine("Waiting for HTTP requests to finish");
try
{
await httpRequestsTask;
}
catch (OperationCanceledException)
{
Console.WriteLine("HTTP requests were cancelled");
}
}
private static async Task GetHttpResponse(HttpClient client, CancellationToken cancellationToken, Action reportStarted, Action reportFinished)
{
var getResponse = client.GetAsync("http://www.google.com", cancellationToken);
reportStarted();
using (var response = await getResponse)
response.EnsureSuccessStatusCode();
reportFinished();
}
}
AusgabeWarum blockiert die Stornierung so lange? Gibt es auch etwas, das ich falsch mache oder das ich besser machen könnte?