问题描述
我在xamarin中开发了我的移动应用程序,并发送了x个请求以显示应用程序的主页。
我的代码如下:
var taskcoll = _mainArticlesAppService.GetWelcomeStartColl().ContinueWith(async (res) =>
{
_applicationContext.Categories = res.Result.Categories;
});
var taskheros = _mainArticlesAppService.GetWelcomeHerosProfileDto().ContinueWith((res) =>
{
_applicationContext.HerosProfileDto = res.Result;
RaisePropertyChanged(() => HerosProfileDto);
});
List<Task> tasksInit = new List<Task>();
tasksInit.Add(taskcoll);
tasksInit.Add(taskheros);
await Task.WhenAll(tasksInit).ConfigureAwait(false);
如果没有问题,一切都会很好并且可以正常工作。
但是,当请求无法连接到远程api服务器时,它将在我的代码中抛出ecxeption,然后死锁,我无法再做任何事情,并且屏幕被冻结。
我发生死锁的代码是:
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)
{
int count = 0;
using (var cts = new CancellationTokenSource(new TimeSpan(0,30)))
{
HttpResponseMessage response = null;
try
{
response = await base.SendAsync(request,cts.Token);
}
catch(Exception ex)
{
throw ex; // --> Deadlock
}
if (response.StatusCode == HttpStatusCode.Unauthorized &&
HasBearerAuthorizationHeader(request))
{
return await HandleUnauthorizedResponse(request,response,cancellationToken);
}
return response;
}
}
解决方法
请勿将async/await
与继续符混合使用,这会使代码不可预测。
同样,无需继续,代码看起来也会更好:
var taskcoll = _mainArticlesAppService.GetWelcomeStartColl(); // start 1st request
var taskheros = _mainArticlesAppService.GetWelcomeHerosProfileDto(); // start 2nd request
// both requests are already running
_applicationContext.Categories = (await taskcoll).Categories; // await 1st request
_applicationContext.HerosProfileDto = await taskheros; // await 2nd one
RaisePropertyChanged(() => HerosProfileDto);
也可以尝试
response = await base.SendAsync(request,cts.Token).ConfigureAwait(false);
对于throw ex
,请确保您已在请求之外使用Exception
处理了try-catch
。当您照原样扔回Exception
并且什么也不做时,在SendAsync
内部,您根本不需要try-catch
。
这是一个抽象示例,其中包含多个异步调用,这些调用以不同的延迟返回数据。没有返回类型,但键入了操作以进行回调。对于我来说,这种方法最准确,可以在完成每个请求后尽快接收不同类型的数据。
class Program
{
static async Task Main(string[] args)
{
SomeController controller = new SomeController();
await controller.LoadAllData();
Console.ReadKey();
}
}
public class SomeController
{
private FirstDataClass _firstProperty;
private SecondDataClass _secondProperty;
private ThirdDataClass _thirdProperty;
public FirstDataClass FirstProperty
{
get => _firstProperty;
set
{
_firstProperty = value;
Console.WriteLine("First data received");
}
}
public SecondDataClass SecondProperty
{
get => _secondProperty;
set
{
_secondProperty = value;
Console.WriteLine("Second data received");
}
}
public ThirdDataClass ThirdProperty
{
get => _thirdProperty;
set
{
_thirdProperty = value;
Console.WriteLine("Third data received");
}
}
public async Task LoadAllData()
{
List<Task> tasks = new List<Task>();
tasks.Add(GetFirstData(data => FirstProperty = data));
tasks.Add(GetSecondData(data => SecondProperty = data));
tasks.Add(GetThirdData(data => ThirdProperty = data));
Console.WriteLine("Tasks launched");
await Task.WhenAll(tasks);
}
private async Task GetFirstData(Action<FirstDataClass> action)
{
await Task.Delay(1000);
action(new FirstDataClass());
}
private async Task GetSecondData(Action<SecondDataClass> action)
{
await Task.Delay(300);
action(new SecondDataClass());
}
private async Task GetThirdData(Action<ThirdDataClass> action)
{
await Task.Delay(500);
action(new ThirdDataClass());
}
}
public class FirstDataClass { }
public class SecondDataClass { }
public class ThirdDataClass { }
控制台输出
Tasks launched
Second data received
Third data received
First data received
如果您不想更改API调用方法,则可以将这种方法用于这些方法的包装,例如:
private async Task GetSomeData(Action<SomeType> action)
{
action(await SomeApiCall());
}
或一些通用包装器
private async Task GetDataWrapper<T>(Func<Task<T>> method,Action<T> action)
{
action(await method);
}