在Windows Phone 8应用程序中正确同步调用异步方法

时间:2014-04-25 09:30:01

标签: c# windows-phone-8 asynchronous windows-phone async-await

我想在Windows Phone 8应用程序中使用可移植类库。 PCL仅包含异步方法,这很好。但我想调用Application_Launching事件处理程序中的一种方法,结果 等待,以便我可以立即使用它主页被加载。

这些类似的问题对我没有帮助:

为了便于复制,以下代码类似但更简单:

PCL

public class TestAsyncClass
{
    public async Task<string> SendRequestAsync()
    {
        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response = await client.GetAsync("http://www.google.com").ConfigureAwait(false);
            return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        }
    }
}

正如您所看到的,正在等待的方法调用有ConfigureAwait(false),因此应该没有死锁,因为该方法想要返回一些捕获的上下文。至少这是我理解Stephen Cleary关于该主题的博客文章的方式:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

App.xaml.cs

private async void Application_Launching(object sender, LaunchingEventArgs e)
{
    TestAsyncLib.TestAsyncClass tac = new TestAsyncLib.TestAsyncClass();
    //string result = await tac.SendRequestAsync(); // GlobalData.MyInitString is still empty  in MainPage.xaml.cs OnNavigatedTo event handler
    string result = tac.SendRequestAsync().Result; // Gets stuck
    GlobalData.MyInitString = result;
}

正如在注释中所写,当异步调用该方法时,GlobalData.MyInitString在MainPage.xaml.cs OnNavigatedTo事件处理程序中尝试访问它时仍为空,因为UI线程立即获得焦点并启动主页面在库方法能够返回任何结果之前。并且同步调用该方法会导致库方法卡住。

这是 MainPage.xaml.cs

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    System.Diagnostics.Debug.WriteLine(GlobalData.MyInitString);
}

为了完整性, GlobalData.cs

public static class GlobalData
{
    public static string MyInitString { get; set; }
}

我很感谢你的帮助!

2 个答案:

答案 0 :(得分:2)

您无法在OnNavigatedTo中使用获取的值,因为OnNavigatedTo与您在Application_Launching中启动的异步方法并行调用。

这是因为Application_Launching具有返回类型void并且无法等待void方法 - 因为它们没有任何有意义的返回; - )

这意味着应用程序已启动,您可以触发某些操作。 然后,应用确定要加载哪个页面并触发您的OnNavigatedTo方法。

现在你可以在那里触发你的异步方法,或者保存一个你可以访问的全局变量来保存结果的任务状态,并检查这个。

让我举一个关于那个任务的例子。 考虑一下Application_Launched方法:

MyStaticClass.Result = tac.SendRequestAsync();

MyStaticClass中的数据定义是:

public static Task<string> Result;

现在在您的电话页面OnNavigatedTo:

if (MyStaticClass.Result != null)
{
    string myResult = await MyStaticClass.Result;
}

您也可以使用单例来代替静态类或属性。

但请记住,OnNavigatedTo方法也是一个void方法,即使您的数据加载可能尚未完成,您的viewModel也已经从底层UI获取读取请求。

答案 1 :(得分:0)

我现在正在回答我自己的问题,但感谢eX0du5指出了正确的方向。

eX0du5指出无法等待void方法,因为它们没有返回任务。搜索“Application_Launching returns Task”让我看到一篇很棒的msdn博客文章,详细解释了所有内容:

http://blogs.msdn.com/b/andy_wigley/archive/2013/07/31/beware-the-perils-of-async-await-in-application-lifecycle-event-handlers-in-fact-in-any-event-handlers.aspx

事情是:事件处理程序无法返回任务,并且在应用程序生命周期事件处理程序中调用异步方法的建议是:“不要。”

此外,在博客文章的最后一段中,作者说在异步方法上使用.Wait()可能有效,但是当UI线程引起调度程序的注意时,它可能会死锁。

因此解释了我对两种不同方法的两个问题。现在做什么? eX0du5的建议(将任务而不是数据保存到GlobalData静态类)是一种解决方法。如果需要从不同的页面访问应用程序启动时请求的数据,这似乎是一个很好的解决方案,如果仅在启动应用程序后未加载的页面上需要数据,它甚至似乎就像预取数据一个非常好的解决方案。我刚刚检查过任务是在创建时执行的,而不仅仅是等待.Result.Wait()this code一起使用时。

出于我的目的,因为我只使用GlobalData来使用MainPage.xaml.cs中的数据,这些数据在启动应用程序时被加载,所以似乎不那么混乱,更简洁,不使用Application_Launching事件处理程序alltogether,而是在OnNavigatedTo事件处理程序中执行所有操作。这就是Noseratio对我的问题的评论派上用场的问题,以及关于SO的问题:When should I load data in a Windows Phone 8 application?