У меня возникла проблема взаимоблокировки с вызовами HttpClient.Я предполагаю, что тупик - результат моих неполных знаний об асинхронном программировании, и я делаю что-то не так.Я упростил фактический код в приведенном ниже примере, но я сохранил ту же последовательность и структуру вызова метода.Я буду благодарен за некоторые подробности о том, что может быть причиной тупика, и за предложения по преодолению этой проблемы.
Я создал класс расширения для HttpClient, который имеет следующие методы:
//HttpClientExtension class
public async static Task<HttpResponseMessage> PostAsyncCustom(this HttpClient client, Uri uri, HttpContent request, bool tryReauth)
{
var originalResponse = await client.PostAsync(uri, request);
var content = await originalResponse.Content.ReadAsStringAsync();
if(tryReauth)
{
//check if session expired and login again
var login = await App.Communication.Login();
//throw exception with the reauth status
if(login)
{
throw new AuthException(true);
}
else
{
throw new AuthException(false);
}
}
}
public async static Task<HttpResponseMessage> PostAsyncWithReauth(this HttpClient client, Uri uri, HttpContent request)
{
return await PostAsyncCustom(client, uri, request, true);
}
public async static Task<HttpResponseMessage> PostAsyncWithoutReauth(this HttpClient client, Uri uri, HttpContent request)
{
return await PostAsyncCustom(client, uri, request, false);
}
Эти методы используются внутри моего класса Communication, который выглядит примерно так:
//Communication class
//HttpClient is defined in contstructor
public async Task<bool> Login()
{
//Define Uri and Request
var response = await client.PostAsyncWithoutReauth(uri, request);
//Check response status and return success/failure
return true;
}
public async Task<bool> FetchData()
{
//Define Uri and Request
try
{
var response = await client.PostAsyncWithReauth(uri, request);
}
catch(AuthException ae)
{
//if reauth was successfull
if(ae)
{
var newResponse = await client.PostAsyncWithoutReauth(uri, request);
//Check newResponse status and return success/failure
}
}
return false;
}
Итак, что происходит, когда AuthException
выдается с status = true и будет вызываться PostAsyncWithoutReauth
, чтослучается, что точка останова внутри PostAsyncWithoutReauth
будет срабатывать, но при продолжении точка останова внутри PostAsyncCustom
, которая вызывается из PostAsyncWithoutReauth
, никогда не срабатывает.Код будет продолжать «выполняться», пока не сработает исключение тайм-аута задачи.Вот почему я думаю, что это тупиковая проблема.Я попытался установить .ConfigureAwait(false)
для некоторых вызовов, но проблема осталась прежней.
Обновление:
Метод FetchData
вызывается изXamarin.Forms ContentPage, где он вызывается завершенным событием в поле «Запись»:
Input.Completed += async (s, e) =>
{
var status = await App.Communication.FetchData();
}
Я хотел бы добавить, что если вместо вызова PostAsyncWithoutReauth
я заменю его на return await FetchData()
, это не будеттупик.
Обновление № 2: Я упростил код, поэтому мой PostAsyncCustom
теперь выглядит так:
public async static Task<HttpResponseMessage> PostAsyncCustom(this HttpClient client, Uri uri, HttpContent request, bool tryReauth)
{
var originalResponse = await client.PostAsync(uri, request);
var content = await originalResponse.Content.ReadAsStringAsync();
if(tryReauth)
{
return await PostAsyncCustom(client, uri, request, false);
}
return originalResponse;
}
Что здесь происходит, так это то, что client.PostAsync
будет зависать при вызове во второй раз.Я также попытался избавиться от рекурсии, поэтому я протестировал код следующим образом:
public async static Task<HttpResponseMessage> PostAsyncCustom(this HttpClient client, Uri uri, HttpContent request, bool tryReauth)
{
var originalResponse = await client.PostAsync(uri, request);
var content = await originalResponse.Content.ReadAsStringAsync();
if(tryReauth)
{
var secondReponse = await client.PostAsync(uri, request);
}
return originalResponse;
}
Он также будет зависать при повторном вызове client.PostAsync
.
Update #3:
"тупик", наконец, вызывает исключение с помощью трассировки стека, которое можно увидеть здесь .