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

本文关键字:同步 调用 异步方法 Phone 应用程序 Windows | 更新日期: 2023-09-27 17:59:57

我想在Windows Phone 8应用程序中使用可移植类库。PCL只包含异步方法,这很好。但是我想从Application_Launching事件处理程序中调用其中一个方法,并等待结果,这样我就可以在主页加载后立即使用它。

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

  • Windows Phone 8上同步的HttpWebRequest
  • 在windowsphone 8中调用synchronized方法中的异步方法

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

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

应用程序.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;
}

正如注释中所写,当异步调用该方法时,在MainPage.xaml.cs OnNavigatedTo事件处理程序中尝试访问GlobalData.MyInitString时,它仍然为空,因为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; }
}

我感谢你的帮助!

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

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

这是因为Application_Launching的返回类型为void,不能等待void方法-因为它们没有任何有意义的返回;-)

这意味着应用程序已经启动,你可以触发一些事情来做。然后应用程序确定要加载的页面,并启动OnNavigatedTo方法。

现在,您可以在那里触发异步方法,也可以保存一个全局变量,您可以访问该全局变量来保存结果的任务状态,并根据该全局变量进行检查。

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

MyStaticClass.Result = tac.SendRequestAsync();

MyStaticClass中的数据定义为:

public static Task<string> Result;

现在在您的手机页面上导航到:

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

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

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

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

eX0du5指出,不能等待void方法,因为它们没有返回Task。搜索"Application_Launching-returingTask"让我看到了一篇很棒的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

问题是:事件处理程序不能返回Tasks,在应用程序生命周期事件处理器中调用异步方法的建议是:"不要。"

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

因此,我用两种不同的方法遇到的两个问题都得到了解释。现在该怎么办?eX0du5的建议(将Task而不是数据保存到GlobalData静态类中)是一种变通方法。如果在应用程序启动时请求的数据需要从不同的页面访问,这似乎是一个很好的解决方案,如果数据只需要在启动应用程序后没有加载的页面上,这甚至是一个非常好的预取数据的解决方案。我刚刚检查了任务在创建时是否执行,而不仅仅是在等待或与.Result.Wait()一起使用此代码时。

不过,出于我的目的,由于我只使用GlobalData来使用启动应用程序时加载的MainPage.xaml.cs中的数据,因此不同时使用Application_Launching事件处理程序,而是在OnNavigatedTo事件处理程序中执行所有操作似乎不那么令人困惑,也更简洁。这就是Noseratio对我的问题的评论派上用场的地方,也是关于SO的问题:我应该什么时候在Windows Phone 8应用程序中加载数据。