#c# #multithreading #http
#c# #многопоточность #http
Вопрос:
Я новичок в этом языке, новичок в HttpClient и плохо разбираюсь в многопоточности, так что постарайтесь потерпеть меня. У меня есть приложение Windows Forms, которое позволяет пользователю вводить идентификатор. Когда они нажимают «Пуск», он начнет повторно отправлять запросы к веб-API определенного веб-сайта (пока они не нажмут «Стоп»), каждый раз увеличивая идентификатор на 1. Идентификатор включается в запросы; это заставляет сайт возвращать таблицу JSON виртуального объекта, связанного с этим идентификатором. Тысячи этих объектов загружаются на сайт в минуту.
Вот метод, который я использую для выполнения запросов:
private async Task<string> GetResponseText(HttpClient client, string address)
{
return await client.GetStringAsync(address);
}
Итак, в той части моего кода, где я обрабатываю, что происходит, когда программа собирается начать сканирование, я сначала создаю новый HttpClient, например, так:
HttpClient client = new HttpClient();
Затем я создаю совершенно новый поток и начинаю цикл:
System.Threading.Thread scanThread = new System.Threading.Thread(() =>
{
while (scanning)
{
string assetUrl = "https://api.example.com/blah?id=" assetId.ToString();
string response = GetResponseText(client, assetUrl).Resu<
dynamic assetJson = JsonConvert.DeserializeObject(response);
if (assetJson != null)
{
this.Invoke((MethodInvoker)delegate
{
try
{
// omitted lots of irrelevant stuff
assetId = 1;
}
catch (RuntimeBinderException)
{
// this probably means the ID doesn't exist yet so I do nothing
}
});
}
}
System.Threading.Thread.CurrentThread.Abort();
});
scanThread.IsBackground = true;
scanThread.Start();
Когда я запускал это в первый раз, все работало нормально. Это удалось с каждым отдельным идентификатором, и он отправлял не менее 5 или 10 запросов в секунду. Не было никаких странных исключений или проблем за кулисами или чего-то подобного. Но затем, как гром среди ясного неба, после, может быть, сотни или двух запросов, у меня возникла моя первая проблема:
A first chance exception of type 'System.Net.Http.HttpRequestException' occurred in mscorlib.dll
A first chance exception of type 'System.AggregateException' occurred in mscorlib.dll
An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll
Additional information: One or more errors occurred.
Трассировка стека сузила проблему до этой строки:
string response = GetResponseText(client, assetUrl).Resu<
«Все в порядке», — подумал я про себя. Я не знал, что было не так, поэтому я настроил блок try / catch для этой строки. И это та часть, которая действительно заставляет меня почесать затылок.
С помощью try / catch на месте, как только возникает эта ошибка, она просто продолжает повторяться снова и снова для всего цикла. Это никогда не восстанавливается и не продолжается нормально. И есть два способа исправить это, второй из которых самый странный:
1) Просто закройте программу и откройте ее снова
2) Остановите сканирование, терпеливо подождите, пока в отладочном выводе Visual Studio появится около 6 строк о выходе потоков, затем снова запустите сканирование
Этот второй сценарий, который, как мне кажется, является ключом к пониманию того, почему это происходит, но я недостаточно хорош, чтобы разобраться в этом. Что происходит? И почему ожидание закрытия некоторых потоков (с чего бы они ни начинались) устраняет проблему? Могу ли я просто избавляться от них вручную каждый раз, когда выполняется цикл?
Комментарии:
1. Пожалуйста, попробуйте тот же подход, используя основной поток. Поэтому удалите свой поток и передайте цикл while непосредственно в вашей форме. Теперь в конце вашего цикла добавьте приложение. DoEvents(); Так что вы можете отреагировать и нажать кнопку stop или sth. Это не решение, но если это поможет вам сейчас, то только ваша потоковая обработка вызывает эти проблемы.
2. Углубитесь в AggregateExceptions и узнайте, каковы внутренние исключения
3. @Sebi Application. DoEvents() остановил ответ всего приложения, даже если по какой-то причине оно было помещено внутри цикла while
4. @AntP Я не уверен, как это сделать
5. @Autumn вводите точку останова, когда вы перехватываете исключения и проверяете
InnerExceptions
свойство.
Ответ №1:
HTTP-Statuscode 400 означает, что ваш запрос недействителен. Поэтому вам нужно подробно проверить свой HTTP-запрос. Возможно, API разрешает только ограниченное количество access или sth.
Далее я приведу вам несколько примеров кода, которые являются более современными:
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
private void Start()
{
var task = Task.Factory.StartNew(() => {
var client = new HttpClient();
while(true)
{
if (ct.IsCancellationRequested)
{
break;
}
string assetUrl = "https://api.example.com/blah?id=" assetId;
string response = await GetResponseText(client, assetUrl);
dynamic assetJson = JsonConvert.DeserializeObject(response);
if (assetJson != null)
{
assetId ;
}
}
}, ct).ContinueWith(ex => Console.WriteLine(ex.Message), TaskContinuationOptions.OnlyOnFaulted);
}
private void Stop()
{
ct.Cancel();
}
Комментарии:
1. Спасибо. Я совершенно уверен, что проблема связана с этим конкретным API, и я рассмотрю это в следующий раз, когда смогу. И современный код полезен.
2. @Осень, добро пожаловать. Да, я думаю, что api тоже будет проблемой.