【问题标题】:Proper synchronous call of async method in Windows Phone 8 appWindows Phone 8 应用程序中异步方法的正确同步调用
【发布时间】:2014-04-25 09:30:00
【问题描述】:

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

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

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

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;
}

如cmets中所写,异步调用该方法时,在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; }
}

感谢您的帮助!

【问题讨论】:

  • 与其进行阻塞等待,您可能应该重新考虑您的 UI 模型并将awaitindefinite progress indicator 一起使用,然后在SendRequestAsync 完成时加载正常的 UI。

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


【解决方案1】:

您无法在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 获得读取请求。

【讨论】:

    【解决方案2】:

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

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

    问题是:事件处理程序不能返回任务,而在应用生命周期事件处理程序中调用异步方法的建议是:“不要”。 p>

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

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

    但出于我的目的,由于我仅使用 GlobalData 来使用启动应用程序时加载的 MainPage.xaml.cs 中的数据,因此不使用 @987654331 似乎更容易混淆且更简洁@ 事件处理程序,而是在 OnNavigatedTo 事件处理程序中执行所有操作。这就是Noseratio对我的问题的评论派上用场的地方,也是关于SO的这个问题:When should I load data in a Windows Phone 8 application?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多