私はWindows Metroアプリケーションを開発していますが、UIが応答しなくなるという問題を抱えています。私が言うことができる限り、原因は以下の通りです:
    <ListView
...
        SelectionChanged="ItemListView_SelectionChanged"            
...
このイベントはここで処理されます。
    async void ItemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (this.UsingLogicalPageNavigation()) this.InvalidateVisualState();
        MyDataItem dataItem = e.AddedItems[0] as MyDataItem;
        await LoadMyPage(dataItem);
    }
    private async Task LoadMyPage(MyDataItem dataItem)
    {            
        SyndicationClient client = new SyndicationClient();
        SyndicationFeed feed = await client.RetrieveFeedAsync(new Uri(FEED_URI));                    
        string html = ConvertRSSToHtml(feed)
        myWebView.NavigateToString(html, true);            
    }
LoadMyPageWebサービスからデータを取得して画面にロードするため、完了までに時間がかかります。しかし、UIがそれを待っているように見えます。私の推測では、上記のイベントが完了するまでです。
だから私の質問です:私はこれについて何ができますか?私が引っ掛けることができるよりよいイベントがありますか、またはこれを処理する別の方法がありますか?私はバックグラウンドタスクを開始することを考えました、しかしそれは私にとってやり過ぎのようです。
編集:
この問題の規模を明確にするために、最大3〜4秒で応答しない状態にしています。これは決して長期の仕事ではありません。
編集:
私は以下の提案のいくつかを試してみました、しかし、全体からのコールスタックSelectionChanged関数はasync / awaitを使用しています。私はこの声明にそれを追跡しました:
myFeed = await client.RetrieveFeedAsync(uri);
それが完了するまで処理を続けているように見えません。
編集:
私はこれが戦争になっていることを理解しています。平和だが、以下は空白の地下鉄アプリとボタンを使った問題の再現である。
XAML:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <Button Click="Button_Click_1" Width="200" Height="200">test</Button>
        <TextBlock x:Name="test"/>
    </StackPanel>
</Grid>
コードビハインド:
    private async void Button_Click_1(object sender, RoutedEventArgs e)
    {
        SyndicationFeed feed = null;
        SyndicationClient client = new SyndicationClient();
        Uri feedUri = new Uri(myUri);
        try
        {
            feed = await client.RetrieveFeedAsync(feedUri);
            foreach (var item in feed.Items)
            {       
                test.Text += item.Summary.Text + Environment.NewLine;                    
            }
        }
        catch
        {
            test.Text += "Connection failed\n";
        }
    }
                    
                
                
                                    
これを試してみる...
SyndicationFeed feed = null;
SyndicationClient client = new SyndicationClient();
var feedUri = new Uri(myUri);
try {
    var task = client.RetrieveFeedAsync(feedUri).AsTask();
    task.ContinueWith((x) => {
        var result = x.Result;
        Parallel.ForEach(result.Items, item => {
            Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
            () =>
            {
                test.Text += item.Title.Text;
            });
       });     
   });
}
catch (Exception ex) { }
私は自分のマシンでGridアプリテンプレートを使ってボタンをアプリに追加することで試しました。ページのタイトルを問題なく更新しながら、アイテムのグリッドを前後にスクロールできます。それほど多くのアイテムを持っていなかったけれども、それは本当に速く動いたので、それは100%ポジティブであるのは難しいことでした。
ForEach) - ありがとう。私は次のような印象を受けました。await事実上これと同じことをした - だからこれは何をするのかawaitしないのですか? - pm_2
あなたが使っているのでawaitの前にLoadMyPage私はそれがコンパイルされ、それがTask。それを考えると、私は小さな例を作成しました。
それを仮定しましょうLoadMyPage(そしてSleep())このようになります:
public Task<string> LoadMyPage()
{
    return Task<string>.Factory.StartNew(() =>
                                                {
                                                    Sleep(3000);
                                                    return "Hello world";
                                                });
}
static void Sleep(int ms)
{
    new ManualResetEvent(false).WaitOne(ms);
}
そしてそれはXAMLこのようになります:
<StackPanel>
    <TextBlock x:Name="Result" />
    <ListView x:Name="MyList" SelectionChanged="ItemListView_SelectionChanged">
        <ListViewItem>Test</ListViewItem>
        <ListViewItem>Test2</ListViewItem>
    </ListView>
    <Button>Some Button</Button>
    <Button>Some Button2</Button>
</StackPanel>
それから、SelectionChangedこのようなイベントハンドラ
private async void ItemListView_SelectionChanged(object sender,
                                                 SelectionChangedEventArgs e)
{
    MyList.IsEnabled = false;
    var result = await LoadMyPage();
    Result.Text = result;
    MyList.IsEnabled = true;
}
のTaskそれLoadMyPage戻り値は並列に実行されます。つまり、そのタスクが実行されているときは、UI凍ってはいけません。今からその結果を得るためにTaskあなたが使うawait。これにより継続ブロックが作成されます。
この例では、何かを選択すると、ListViewロード時間全体にわたって無効になり、Task終わりました。ボタンがまだ反応していることを確認するためにボタンを押すことで、UIがフリーズしなかったことを確認できます。
もしLoadMyPageUIとやり取りするには、少し調整し直す必要があります。ViewModelあるいはあなたが望む結果、そしてUIスレッド上ですべてを再びまとめる。
RetrieveFeedAsync - pm_2Sleepを使ってManualResetEvent(false).WaitOne(ms)。私の答えがどのようなものではないのかわかりません。asyncクリアしてもらえますか。 - Filip EkbergTask非同期的に実行されますawaitedにItemListView_SelectionChanged。 - Filip Ekberg
バックグラウンドスレッドは最も確実ですではないやり過ぎです。それこそまさにあなたがこの種の問題を扱う方法です。
UIスレッド上で長時間のタスクを実行しないでください。さもないと、UIが拘束されて応答しなくなります。これらをバックグラウンドスレッドで実行してから、そのスレッドにメインUIスレッドが終了したときに処理できるイベントを発生させます。
UIスレッドに何らかの進行状況インジケーターを表示することも便利です。ユーザーはそれを知りたい何かが起こっている。これにより、アプリが壊れたりフリーズしたりしていないことを安心させることができ、もう少し待つ必要があります。これが、すべてのWebブラウザになんらかの「ドキドキ」やその他の負荷インジケータがある理由です。
最も可能性の高い問題はそれですLoadMyPage同期的に何かをしている。覚えて、asyncバックグラウンドスレッドでコードを実行しないでください。デフォルトでは実際のすべてコードUIスレッド上で実行されます(非同期/待つFAQまたは私の非同期/イントロを待つ)したがって、非同期メソッドをブロックしても、呼び出し側スレッドはブロックされたままになります。
を見てみましょうLoadMyPage。使っていますかawaitWebサービスを呼び出すには?データをUIに配置する前に、データの処理に時間がかかるのでしょうか。それはUIを圧倒していますか(多くのWindowsコントロールは、何千もの要素に到達するとスケーラビリティの問題を抱えています)?
LoadMyPage電話するawait Task.Run(() => {}).ConfigureAwait(false)呼び出す前にRetrieveFeedAsync。何が起こるのですか? - Stephen Cleary
あなたの単純化されたコードの例を見て、私はあなたの問題があるべきすべてであると信じます外側待ち行列
次のコードブロックで:
private async void Button_Click_1(object sender, RoutedEventArgs e)
    {
        SyndicationFeed feed = null;
        SyndicationClient client = new SyndicationClient();
        Uri feedUri = new Uri(myUri);
        try
        {
            feed = await client.RetrieveFeedAsync(feedUri);
            foreach (var item in feed.Items)
            {       
                test.Text += item.Summary.Text + Environment.NewLine;                    
            }
        }
        catch
        {
            test.Text += "Connection failed\n";
        }
    }
ののみバックグラウンドスレッドで実行されている行はその行です。
feed = await client.RetrieveFeedAsync(feedUri);
すべてそのブロック内の他のコード行がUIスレッドで実行されています。
ボタンクリックハンドラが非同期としてマークされているからといって、その中のコードがUIスレッド上で実行されないわけではありません。実際、イベントハンドラはUIスレッド上で起動します。そのため、SyndicationClientを作成してUriを設定することは、UIスレッドで行われます。
多くの開発者が気づいていないことは、どんなコードでも来るということです。後にawaitは自動的に使用中のスレッドと同じスレッドで再開します前待っています。これはコードを意味します
            foreach (var item in feed.Items)
            {       
                test.Text += item.Summary.Text + Environment.NewLine;                    
            }
はUIスレッドで実行されています。
これは、Dispatcher.Invokeを実行して更新する必要がないという点で便利です。test.Textしかし、それはまた、アイテムをループしたり文字列を連結したりしている間、UIスレッドをブロックしていることを意味します。
あなたの(単純化されたとはいえ)例では、バックグラウンドスレッドでこの作業を行う最も簡単な方法は、SyndicationClientに別のメソッドを呼び出すことです。RetrieveFeedAsStringAsync;それからSyndicationClientはそれ自身の仕事の一部としてストリングのダウンロード、ループおよび連結をすることができる。そのタスクが完了した後、UIスレッドで実行される唯一のコード行はテキストをTextBoxに割り当てることです。
foreach()ループ。 - pm_2
foreach完全にループ - pm_2