C# 'yi HttpClient ile eşzamansız öğrenmeye çalışıyorum ve neler olduğunu anlayamadığım bir sorunla karşılaştım.Birden çok zaman uyumsuz görevi bir satırda çağırırken (HttpClient kullanarak) Async kodu kısmen engelleniyor gibi görünüyor.
- tek seferde HttpClient birden çok istek gönderme ve yanıtları gelir gelmez iade olarak ayrı ayrı işleme koyduk: Yapmak istediğim ne
. Bu işlem sonsuza kadar bir döngüde tekrarlanır (yani, her birkaç saniyede bir yeni talep istemi). Sonuçlarda gerçekleştirilen işlem CPU yoğun değildir ve yanıt başına en fazla birkaç milisaniye sürer.
- Tek bir durumda 5-50 gönderi var
- Geri gelme sırasına aldırış etmeyin, ancak her istek/yanıt engellemeden mümkün olduğunca hızlı işlem görmelidir.
sorun: Birlikte istekleri içeren bir listenin yollanır ise
, tepkiler hiç bu kadar biraz daha uzun sürecek gibi görünüyor, ancak her biri yaklaşık aynı olmalıdır. örneğin:
Zaman alınan:
Zaman alınan 67,9609 milisaniye:
Zaman alınan 89,2413 milisaniye: 100,2345 milisaniye
Zaman alınan: 117,7308 milisaniye
Zaman alınan: 127,6843 milisaniye
Zaman alınan: 132,3066 milisaniye
Zaman alınan : 157.3057 milisaniye
gerektiği tüm sadece 70ms civarında ... Bu her yeni parti için tekrarlar; Birincisi hızlı, sonra her ardışık biri yavaşlar.
My Code
3 ana parça vardır. Bu gerçek http sorgusu yapar yöntemi içeren
Downloader.cs (Downloader.cs ben zamanlı budur) Sorunun oluştuğunu nereye odaklanmak için gelen en iyi şekilde bunu basitleştirdik:
public async Task<string> DownloadData(string requestString) { HttpResponseMessage response; response = await this.httpClient.PostAsync(url, new StringContent(requestString)); string returnString = await response.Content.ReadAsStringAsync(); return returnString; }
QueryAgent.cs Bu yeni Downloader başlatırken ve arayan fonksiyonu ve bir döngü içinde aramaları uyumsuz yöntemi döngü ana uyumsuz içerir.
Son yanıt kümesinin sonucuna bağlı olarak mümkün olan en kısa zamanda yeni istek göndermem gerekiyor (temelde, isteklerin dışarı çıkma oranı değişebilir, böylece sonunda bir Task.delay() yapıyorum.public async Task DownloadLoopAsync() { while (this.isLooping) { // I put this here to stop it using lots of cpu. // Not sure if it is best practice. // advice on how to do this kind of loop would be appreciated. await Task.Delay(5); foreach (string identifier in this.Identifiers) { string newRequestString = this.generateRequestString(identifier); Task newRequest = RequestNewDataAsync(newRequestString); } } } public async Task RequestNewDataAsync(string requestString) { string responseString = await this.downloader.DownloadData(requestString); this.ProcessedData = this.ProcessData(responseString); }
program.cs) while döngüsünün Bu programı durdurmak için sonunda o zaman uyumsuz QueryAgent içinde işlev ve bir Console.ReadLine() üzerine bir kez Görev çağrılarının sadece bir konsol programıdır çıkarken.HttpClient'i burada da başlatıyorum. Ben gibi, bir kez() DownloadLoop çağırır:
QueryAgent myAgent = new QueryAgent(httpClient); Task dataLoop = myAgent.DownloadLoopAsync(); Console.Readline();
Ben ben DownloadLoopAsync içinde döngü yeni Görevi çağırıyorum yolu ile ilgili bir şey() sahiptir şüphesi Ancak bu bir anlam ifade etmiyor çünkü her bir yeni Görev'in kendi başına işleneceğini varsaydım.
Tuğla duvarına çarptığından, bu çalışmayı tasarlandığı gibi çalıştırmaya çalışırken tuğla duvarına çarptığınız için herhangi bir tavsiye büyük takdir görmektedir.
Teşekkürler. 1
DÜZENLEME Sadece çağıran Görev döngüde etrafında bir karmaşa vardı. Değiştim:
foreach(string identifier in this.Identifiers)
{
string newRequestString = this.generateRequestString(identifier);
Task newRequest = RequestNewDataAsync(newRequestString);
}
şimdi beklendiği gibi çalışmasını sağlayan
foreach(string identifier in this.Identifiers)
{
string newRequestString = this.generateRequestString(identifier);
Task newRequest = RequestNewDataAsync(newRequestString);
await Task.Delay(1);
}
için: Şimdi daha da karıştı bu işe görünmektedir neden olduğum Time taken: 71.0088 milliseconds
Time taken: 65.0499 milliseconds
Time taken: 64.0955 milliseconds
Time taken: 64.4291 milliseconds
Time taken: 63.0841 milliseconds
Time taken: 64.9841 milliseconds
Time taken: 63.7261 milliseconds
Time taken: 66.451 milliseconds
Time taken: 62.4589 milliseconds
Time taken: 65.331 milliseconds
Time taken: 63.2761 milliseconds
Time taken: 69.7145 milliseconds
Time taken: 63.1238 milliseconds
!
DÜZENLEME 2
aşağıdaki gibi zamanlama kodu mevcut Downloader.cs içinde:
public async Task<string> DownloadData(string requestString)
{
HttpResponseMessage response;
Stopwatch s = Stopwatch.StartNew();
response = await this.httpClient.PostAsync(url, new StringContent(requestString));
double timeTaken = s.Elapsed.TotalMilliseconds;
Console.WriteLine("Time taken: {0} milliseconds", timeTaken);
string returnString = await response.Content.ReadAsStringAsync();
return returnString;
}
rağmen bekliyoruz Task.Delay (1); "hile yapmak" gibi görünüyor, ideal bir çözüm değil ne de neden çalıştığını anlayabiliyorum - belki bir şekilde RequestNewDataAsync Görevini başlatması için CPU zamanını verir ve bir çeşit kilitlenmeyi engeller mi? Sadece tahmin edebilirim ... Aynı zamanda, döngü 1000Hz'e sınırladığı anlamına gelir, bu da bir sınırdır, çünkü Task.Delay() yalnızca 1 değeri minimum olan tamsayı değerleri kabul eder.
DÜZENLEME 3
biraz daha okuma (Tüm bu nispeten yeni! Hala) yaparak, belki bu bir şey iyi (kısa ömürlü görevlerin 100s 10) ThreadPool'da kullanılarak bırakılır? Ancak bu, aşırı (yeni görev başına 1 MB) ek yük getirecekti; ya da yine farklı bir şey.
DÜZENLEME 4
- [slowdown gerçekleşir] Downloader'da httpClient.PostAsync() 'den önce veya sonra yerleştirme.CS
- [yavaşlama meydana] önce veya RequestNewDataAsync()
- [yavaşlama/beklenen performans] önce veya DownloadLoopAsync() Görev çağrısının ardından yerleştirme olanağı sunmaktadır çağrısının ardından yerleştirilmesi
nereye istekleri gönderene:
Ayrıca ben kullanıcıya sonra tamamlamak için son toplu beklemek üzere ana programı modifiye kilit bastığında için? sunucunun yükünü koymanız, dolayısıyla daha yavaş yanıt almanız mümkün mü? – Ewan'HttpClient' eşzamanlı olarak kullanılamaz, iş parçacığı güvenli değildir. Ayrıca, "RequestNewDataAsync" ile sonuçlanan görevi hiçbir zaman beklemezsiniz. Böylece, daha öncekiler tamamlanmadan önce daha fazla istek gönderiyorsunuz ve sunucuya daha fazla yük koyuyorsunuz. – Lukazoid
yaklaşımınız standart dışı biraz görünüyor. Ben eklemek ve daha sonra beklediğiniz görevlerin bir liste var öneririm. Döngü yerine – Ewan