在WP8.1上正确调用异步方法

时间:2014-12-24 14:49:31

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

我很抱歉问这样一个基本/愚蠢的问题,但我输了。我花了3天时间处理这个愚蠢的问题并且无法找到解决方案。

我的问题是,一旦涉及异步方法,程序就会冻结。

我的代码如下。首先,我定义一些全局参数,然后在公共MainPage()中,第一个任务是调用方法" OpenFile()"从.txt文件(包含coutries和相应的首都)获取信息

namespace WorldCapitalsv2
{

public sealed partial class MainPage : Page
{
//Some global parameters    
string paysATrouver = null; string capitaleATrouver = null;    
    int scoreJoueur; int nombreParties; int HighScore; Random rand = new Random();int nbCountries;
    List<Pays> listCountries = new List<Pays>();
    HashSet<int> checkRnd = new HashSet<int>();
    int[] propositionRepNb = new int[4];

    public MainPage()
    {
        this.InitializeComponent();
        this.NavigationCacheMode = NavigationCacheMode.Required;
        InitializeComponent();
        openFile(); //Program freezes here :(
        nbCountries = listCountries.Count;
    }

    async void openFile()
    {
        Uri capitaleFileLoc = new Uri("ms-appdata://Capitale.txt");
        StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(capitaleFileLoc);//.AsTask().ConfigureAwait(false);
        var list = await FileIO.ReadLinesAsync(file);//.AsTask().ConfigureAwait(false);
        foreach (string country in list)
        {
            string[] countrycap = country.Split('\t');
            listCountries.Add(new Pays() { nomPays = countrycap[0], Capitale = countrycap[1] });
        }
    }

这非常令人沮丧,因为它在其他方面像魅力一样工作,例如而不是使用.txt文件,我将信息放在主程序中,方法是:

List<Pays> listePays = new List<Pays>(){
            new Pays() {nomPays="France", Capitale="Paris"},
... etc

但是,我宁可打电话和阅读外部文件。

我试图在我的异步openFile方法中添加一个输出参数,例如:

        async Task<List<Pays>> openFile()
{
Code;
return ListCountries;
}

并尝试使用以下命令获取MainPage()中的数据:

  Task<List<Pays>> listePaysT = ouvreFichier();
        listePaysT.Start();
        listePaysT.Wait();
        listePays = listePaysT.Result;

但是这个解决方案没有用......

另外,如果我尝试用await调用我的方法openFile()它将无法工作(我理解等待在MainPage()中无法调用)。

我永远感激能够帮助解决这个问题的人。感谢。

下面有更多详细信息:这是界面:一个非常基本的1页应用程序。

<Page
x:Class="WorldCapitalsv2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WorldCapitalsv2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid>
    <TextBlock x:Name="TexteAppTitle" HorizontalAlignment="Left" Margin="0,0,0,585" TextWrapping="Wrap" Text="World Capitals" Width="400" RenderTransformOrigin="0.5,0.5" FontSize="48"/>
    <Button x:Name="btn_prop1" Content="Button" HorizontalAlignment="Left" Margin="27,261,0,0" VerticalAlignment="Top" Height="70" Width="343" FontSize="24" Click="Reponse1"/>
    <Button x:Name="btn_prop2" Content="Button" HorizontalAlignment="Left" Margin="27,326,0,0" VerticalAlignment="Top" Height="70" Width="343" FontSize="24" Click="Reponse2"/>
    <Button x:Name="btn_prop3" Content="Button" HorizontalAlignment="Left" Margin="27,391,0,0" VerticalAlignment="Top" Height="70" Width="343" FontSize="24" Click="Reponse3"/>
    <Button x:Name="btn_prop4" Content="Button" HorizontalAlignment="Left" Margin="27,457,0,0" VerticalAlignment="Top" Height="70" Width="343" FontSize="24" Click="Reponse4"/>
    <TextBox x:Name="labelQuestion" HorizontalAlignment="Left" Margin="10,97,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="380" Height="101"/>
    <TextBox x:Name="labelScore" HorizontalAlignment="Left" Margin="27,532,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="145" Height="101"/>
    <TextBox x:Name="labelHighScore" HorizontalAlignment="Left" Margin="223,532,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="145" Height="101"/>

</Grid>

我已将所有方法都放在&#34; MainPage.xaml.cs&#34;只不过最初由VS在&#34; App.xaml.cs&#34;

中创建

3 个答案:

答案 0 :(得分:2)

这是问题所在:

listePaysT.Wait();

这将阻止,直到任务完成...所以你要阻止你的UI线程。更糟糕的是,异步方法需要返回到UI线程才能继续,所以你已经陷入僵局。

Task.Wait()Task.Result只能小心使用非常

你应该考虑到你想要发生的事情。您不能使构造函数异步,但您可能会使页面构造函数完成,然后调用单独的异步方法来填充其数据。 (不清楚为什么你在UI层中获得了所有这些代码 - 不应该是在视图模型中吗?)

基本上,您应该考虑在数据可用之前您想要发生什么 - 您希望UI存在但不包含数据,或者您希望MainPage仅在此之后可用数据已被提取?

您可以使用静态异步方法排序模仿异步构造函数:

public static async MainPage CreatePageAsync()
{
    var ret = new MainPage(); // This would be the normal UI code
    await ret.LoadDataAsync(); // This would asynchronously fetch the data
}

另请注意,目前您已拨打InitializeComponent两次,这不是一个好主意......

答案 1 :(得分:0)

您需要将async void更改为async Task&gt;你之前尝试过的。 然后将构造函数中InitializeComponent之后的代码放入另一个方法(async)并从构造函数中调用它。

不是调用openfile(),而是将其更改为listCountries = await(openFile());

导致冻结的原因是您在尝试使用结果之前不等待openfile完成。

你应该读一下异步,因为你做错了:) 看看这里:http://blog.stephencleary.com/2012/02/async-and-await.html

你不应该使用&#39; async void&#39;因为没有办法知道异步工作何时完成,异步的主要原因是让长时间运行的代码执行它并报告等待它完成的代码。

答案 2 :(得分:0)

向你求助,几个小时后,我发现了如何继续。 1 /我创建了一个加载按钮/方法来打开.txt文件。然后,我将openFile方法拆分为两个单独的方法(1个用于打开,1个用于拆分)。我还添加了Task&lt;&gt;而不是无效。获取返回值,方法变为:

    public async Task<StorageFile> openFile()
    {
        Uri capitaleFileLoc = new Uri("ms-appx:///Assets/Capitale.txt");
        StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(capitaleFileLoc);//.AsTask().ConfigureAwait(false);
        return file;
    }

    public async Task<List<Pays>> splitFile(StorageFile file)
    {
       var list = await FileIO.ReadLinesAsync(file);//.AsTask().ConfigureAwait(false);
       List<Pays> listCountries = new List<Pays>();

       foreach (string country in list)
       {
           string[] countrycap = country.Split('\t');
           listCountries.Add(new Pays() { nomPays = countrycap[0], Capitale = countrycap[1] });
       }
       labelHighScore.Text =  listCountries[2].nomPays;
       return listCountries;
    }

完成后,调用方法为:

public async void load(object sender, RoutedEventArgs e)
    {
       StorageFile file = await openFile();        
       List<Pays> listCountries = await splitFile(file);
}

这里一切正常。文件已加载,&#34; listCountries&#34;包含所有必需的数据。 (现在我的程序中遇到了不同的新问题,但希望没有任何复杂的问题)。 再次感谢,我希望我的问题能对某人有所帮助。

相关问题