我在MonoAndroid中遇到一个调用WCF服务异步的问题。 我按照本教程编写了Portable Class Lib。 http://nullskull.com/a/10476775/xamarin-cross-platform-application-consuming-wcf--part-1.aspx
这是我的服务方法,它调用WCF服务:
public async static Task<KeyExchangeModel> GetPublicKeyFromServer(KeyExchangeModel model)
{
try
{
ISyncService client;
client = new SyncServiceClient(_binding, _endpointAddress);
var res = Task<KeyExchangeModel>.Factory.FromAsync(client.BeginGetServerPublicKey, client.EndGetServerPublicKey,
model, null);
await res;
return res.Result;
}
catch (Exception e)
{
return null;
}
}
在这里我调用方法并等待它被执行。
Task<KeyExchangeModel> task = SyncServiceAgent.GetPublicKeyFromServer(keyModel);
task.Wait();
KeyExchangeModel serverModel = task.Result;
问题是在Android上我永远不会得到结果。它陷入了一个循环中。设备日志中没有记录异常或被抛出。
此代码完美适用于Windows单元测试和Windows Phone项目。
我希望有人能帮助我。
非常感谢。
答案 0 :(得分:1)
你的问题就在这里:task.Wait();
阻塞异步代码会导致死锁。
默认情况下,await
将捕获当前的“上下文”,并使用该上下文恢复async
方法。在这种情况下,它可能捕获UI上下文,它与UI线程相关联。因此,GetPublicKeyFromServer
将启动WCF调用,捕获UI上下文,并返回不完整的任务。然后调用代码在该任务上调用Task.Wait
,阻止 UI线程,直到该异步方法完成。
稍后,WCF调用返回,GetPublicKeyFromServer
尝试在相同的上下文(在UI线程上)恢复。但是,UI线程被阻止等待GetPublicKeyFromServer
完成。这是一个典型的僵局。
它在单元测试中工作的原因是因为async
方法捕获线程池上下文而不是UI上下文,因此它能够阻止Wait
中的一个线程池线程和< em>另一个线程池线程可以完成async
方法。通常情况下,Windows Phone应用程序会遇到与Android应用程序相同的UI上下文问题,所以我怀疑测试代码有些不同,这就是为什么它不会在WP上死锁。
我更详细地描述了这个问题on my blog,in an MSDN article和in my book。
此问题的最佳解决方案是使用await
代替Task.Wait
或Task<T>.Result
。即,您的调用代码应为:
Task<KeyExchangeModel> task = SyncServiceAgent.GetPublicKeyFromServer(keyModel);
KeyExchangeModel serverModel = await task;
这将要求您的调用代码为async
,这反过来要求其调用者为async
等。通过代码库的异步“增长”是自然而正常。