Migration von Threadingfunktionen

In diesem Thema wird beschrieben, wie Sie den Threadingcode in einer Universelle Windows-Plattform (UWP)-Anwendung zum Windows App SDK migrieren.

Zusammenfassung der API- und/oder Funktionsunterschiede

Das Threadingmodell von UWP ist eine Variante des Single-Threaded-Apartment-(STA)-Modells namens Application STA (ASTA), das Reentranz verhindert und dazu beiträgt, verschiedene Reentranzfehler und Deadlocks zu vermeiden. Ein ASTA-Thread wird auch als UI-Thread bezeichnet.

Das Windows App SDK verwendet ein Standard-STA-Threadingmodell, das nicht dieselben Schutzmechanismen für die Eintrittsvarianz bietet.

Der CoreDispatcher-Typ wird zu DispatcherQueue migriert. Und die CoreDispatcher.RunAsync-Methode wird zu DispatcherQueue.TryEnqueue migriert.

C++/WinRT. Wenn Sie winrt::resume_foreground mit CoreDispatcher verwenden, migrieren Sie dies stattdessen, um DispatcherQueue zu verwenden.

ASTA zu STA-Threadingmodell

Weitere Details zum ASTA-Threadingmodell finden Sie im Blogbeitrag Was ist so speziell für das Application STA?.

Da das STA-Threadingmodell des Windows App SDK nicht die gleiche Gewähr dafür bietet, Reentranzprobleme zu verhindern, funktioniert Ihr Code möglicherweise nicht wie erwartet, wenn Ihre UWP-App vom nicht reentranten Verhalten des ASTA-Threadingmodells ausgeht.

Ein Punkt, auf den Sie achten sollten, ist die Eintrittsvarianz bei XAML-Steuerelementen (siehe das Beispiel in Eine Windows App SDK-Migration der Beispiel-App für den UWP-Foto-Editor (C++/WinRT)). Und bei einigen Abstürzen, etwa bei Zugriffsverletzungen, ist der direkte Absturz-Callstack in der Regel der richtige Stack, der zu verwenden ist. Wenn es sich jedoch um einen Absturz aufgrund einer stowed exception handelt – mit dem Ausnahmecode 0xc000027b –, ist mehr Aufwand erforderlich, um den korrekten Callstack zu erhalten.

Stowed-Ausnahmen

Abstürze durch Stowed-Ausnahmen speichern einen möglichen Fehler zwischen, auf den später zurückgegriffen wird, wenn kein Codeabschnitt die Ausnahme behandelt. XAML entscheidet manchmal sofort, dass der Fehler fatal ist. In diesem Fall kann der direkte Absturz-Stack hilfreich sein. Aber häufiger wurde der Stack abgewickelt, bevor festgestellt wurde, dass er fatal war. Weitere Informationen zu „stowed exceptions“ finden Sie in der Folge von Inside Show Stowed Exception C000027B.

Bei Abstürzen aufgrund von Stowed-Ausnahmen (um eine geschachtelte Nachrichtenpumpe zu sehen oder um zu sehen, wie die spezifische Ausnahme des XAML-Steuerelements ausgelöst wird) können Sie weitere Informationen zum Absturz erhalten, indem Sie eine Absturzabbilddatei im Windows-Debugger (WinDbg) laden (siehe Debugtools für Windows herunterladen) und dann !pde.dse verwenden, um die Stowed-Ausnahmen auszugeben.

Die PDE-Debuggererweiterung (für den Befehl) ist eine von der !pde.dse Community bereitgestellte WinDbg-Erweiterung. Suchen Sie nach „pde WinDbg extension“, um den aktuellen Downloadort zu finden. Fügen Sie die entsprechende x64- oder x86-Datei .dll aus dem Paket in das Verzeichnis winext Ihrer WinDbg-Installation ein, und dann funktioniert !pde.dse mit Absturzabbilddateien zu Stowed-Ausnahmen.

Häufig gibt es mehrere Stowed-Ausnahmen, wobei einige am Ende behandelt/ignoriert wurden. Am häufigsten ist die erste Stowed-Ausnahme die interessante Ausnahme. In einigen Fällen kann die erste Stowed-Ausnahme ein erneutes Auslösen der zweiten Ausnahme sein. Wenn also die zweite Stowed-Ausnahme in derselben Aufrufliste weiter unten erscheint als die erste, kann die zweite Ausnahme der Ursprung des Fehlers sein. Der Fehlercode, der bei jeder gespeicherten Ausnahme angezeigt wird, ist ebenfalls wertvoll, da dadurch das der Ausnahme zugeordnete HRESULT angegeben wird.

Ändern von "Windows.UI.Core.CoreDispatcher" in "Microsoft.UI.Dispatching.DispatcherQueue"

Dieser Abschnitt gilt, wenn Sie die Windows.UI.Core.CoreDispatcher-Klasse in Ihrer UWP-App verwenden. Dazu gehört die Verwendung von Methoden oder Eigenschaften, die einen CoreDispatcher entgegennehmen oder zurückgeben, wie z. B. die DependencyObject.Dispatcher- und CoreWindow.Dispatcher-Eigenschaften. Sie rufen beispielsweise DependencyObject.Dispatcher auf, wenn Sie den CoreDispatcher abrufen, der zu einem Windows.UI.Xaml.Controls.Page gehört.

// MainPage.xaml.cs in a UWP app
if (this.Dispatcher.HasThreadAccess)
{
    ...
}
// MainPage.xaml.cpp in a UWP app
if (this->Dispatcher().HasThreadAccess())
{
    ...
}

Stattdessen müssen Sie in Ihrer Windows App SDK-App die Microsoft.UI.Dispatching.DispatcherQueue-Klasse verwenden. Und die entsprechenden Methoden oder Eigenschaften, die eine DispatcherQueue akzeptieren oder zurückgeben, wie die DependencyObject.DispatcherQueue- und Microsoft.UI.Xaml.Window.DispatcherQueue-Eigenschaften. Beispielsweise rufen Sie DependencyObject.DispatcherQueue auf, wenn Sie die DispatcherQueue abrufen, die zu einer Microsoft.UI.Xaml.Controls.Page gehört (die meisten XAML-Objekte sind DependencyObjects).

// MainPage.xaml.cs in a Windows App SDK app
if (this.DispatcherQueue.HasThreadAccess)
{
    ...
}
// MainPage.xaml.cpp in a Windows App SDK app
#include <winrt/Microsoft.UI.Dispatching.h>
...
if (this->DispatcherQueue().HasThreadAccess())
{
    ...
}

Ändern Sie CoreDispatcher.RunAsync in DispatcherQueue.TryEnqueue

Dieser Abschnitt gilt, wenn Sie die Windows.UI.Core.CoreDispatcher.RunAsync-Methode verwenden, um eine Aufgabe für den Haupt-UI-Thread (oder für den Thread zu planen, der einem bestimmten Windows.UI.Core.CoreDispatcher zugeordnet ist).

// MainPage.xaml.cs in a UWP app
public void NotifyUser(string strMessage)
{
    if (this.Dispatcher.HasThreadAccess)
    {
        StatusBlock.Text = strMessage;
    }
    else
    {
        var task = this.Dispatcher.RunAsync(
            Windows.UI.Core.CoreDispatcherPriority.Normal,
            () => StatusBlock.Text = strMessage);
    }
}
// MainPage.cpp in a UWP app
void MainPage::NotifyUser(std::wstring strMessage)
{
    if (this->Dispatcher().HasThreadAccess())
    {
        StatusBlock().Text(strMessage);
    }
    else
    {
        auto task = this->Dispatcher().RunAsync(
            Windows::UI::Core::CoreDispatcherPriority::Normal,
            [strMessage, this]()
            {
                StatusBlock().Text(strMessage);
            });
    }
}

Verwenden Sie in Ihrer Windows App SDK-App stattdessen die Methode Microsoft.UI.Dispatching.DispatcherQueue.TryEnqueue. Sie fügt der Microsoft.UI.Dispatching.DispatcherQueue eine Aufgabe hinzu, die auf dem thread ausgeführt wird, der der DispatcherQueue zugeordnet ist.

// MainPage.xaml.cs in a Windows App SDK app
public void NotifyUser(string strMessage)
{
    if (this.DispatcherQueue.HasThreadAccess)
    {
        StatusBlock.Text = strMessage;
    }
    else
    {
        bool isQueued = this.DispatcherQueue.TryEnqueue(
        Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal,
        () => StatusBlock.Text = strMessage);
    }
}
// MainPage.xaml.cpp in a Windows App SDK app
#include <winrt/Microsoft.UI.Dispatching.h>
...
void MainPage::NotifyUser(std::wstring strMessage)
{
    if (this->DispatcherQueue().HasThreadAccess())
    {
        StatusBlock().Text(strMessage);
    }
    else
    {
        bool isQueued = this->DispatcherQueue().TryEnqueue(
            Microsoft::UI::Dispatching::DispatcherQueuePriority::Normal,
            [strMessage, this]()
            {
                StatusBlock().Text(strMessage);
            });
    }
}

Migrieren von winrt::resume_foreground (C++/WinRT)

Dieser Abschnitt gilt, wenn Sie die Winrt::resume_foreground-Funktion in einer Coroutine in Ihrer C++/WinRT-UWP-App verwenden.

In UWP besteht der Anwendungsfall für "winrt::resume_foreground" darin, die Ausführung in einen Vordergrundthread zu wechseln (dieser Vordergrundthread ist häufig der Thread, der einem Windows.UI.Core.CoreDispatcher zugeordnet ist). Hier ist ein Beispiel dafür.

// MainPage.cpp in a UWP app
winrt::fire_and_forget MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    ...
    co_await winrt::resume_foreground(this->Dispatcher());
    ...
}

In Ihrer Windows App SDK-App:

Fügen Sie also zuerst einen Verweis auf das Microsoft.Windows.ImplementationLibrary NuGet-Paket hinzu.

Fügen Sie dann das folgende Include zu pch.h im Zielprojekt hinzu.

#include <wil/cppwinrt_helpers.h>

Und folgen Sie dann dem unten gezeigten Muster.

// MainPage.xaml.cpp in a Windows App SDK app
...
winrt::fire_and_forget MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    ...
    co_await wil::resume_foreground(this->DispatcherQueue());
    ...
}

Siehe auch