异步WCF在无限循环中调用单声道卡

时间:2014-06-12 10:00:19

标签: c# mono xamarin xamarin.android

我在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项目。

我希望有人能帮助我。

非常感谢。

1 个答案:

答案 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 blogin an MSDN articlein my book

此问题的最佳解决方案是使用await代替Task.WaitTask<T>.Result。即,您的调用代码应为:

Task<KeyExchangeModel> task = SyncServiceAgent.GetPublicKeyFromServer(keyModel);
KeyExchangeModel serverModel = await task;

这将要求您的调用代码为async,这反过来要求调用者为async等。通过代码库的异步“增长”是自然而正常。