Freigeben über


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 Sta-Modells (Single-Threaded Apartment) namens Application STA (ASTA), das die Reentranz blockiert und dabei hilft, verschiedene Reentranzfehler und Deadlocks zu vermeiden. Ein ASTA-Thread wird auch als UI-Thread bezeichnet.

Das Windows App SDK verwendet ein standardmäßiges STA-Threadingmodell, das nicht die gleichen Reentrancy-Garantien bereitstellt.

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 über die gleichen Sicherheiten verfügt, um Reentrancy-Probleme zu verhindern, verhält sich der Code möglicherweise nicht wie erwartet, wenn Ihre UWP-App das Verhalten der nicht erneuten Einsteiger des ASTA-Threadingmodells annimmt.

Beachten Sie, dass die XAML-Steuerelemente neu intrahiert werden (siehe Beispiel in einer Windows App SDK-Migration der UWP-Foto-Editor-Beispiel-App (C++/WinRT)). Und für einige Abstürze, z. B. Zugriffsverletzungen, ist der direkte Absturzaufrufstack in der Regel der richtige Stapel, der verwendet werden soll. Wenn es sich jedoch um einen verstauten Ausnahmeabsturz handelt – der Ausnahmecode hat: 0xc000027b – ist mehr Arbeit erforderlich, um den richtigen Aufrufstapel abzurufen.

Verstaute Ausnahmen

Fehlerhafte Ausnahmeabstürzen speichern einen möglichen Fehler, der später verwendet wird, wenn kein Teil des Codes die Ausnahme behandelt. XAML entscheidet manchmal, dass der Fehler sofort schwerwiegend ist, in diesem Fall kann der direkte Absturzstapel gut sein. Aber häufiger hat der Stapel entwundt, bevor er festgestellt wurde, tödlich zu sein. Weitere Informationen zu verstauten Ausnahmen finden Sie in der Inside Show-Episode "Stowed Exception C000027B".

Für verstaute Ausnahmeabstürzen (zum Anzeigen einer geschachtelten Meldungspumpe oder zum Anzeigen der spezifischen Ausnahme des XAML-Steuerelements, die ausgelöst wird), können Sie weitere Informationen zum Absturz erhalten, indem Sie ein Absturzabbild im Windows-Debugger (WinDbg) laden (siehe Downloaddebuggingtools für Windows), und anschließend die !pde.dse verstauten Ausnahmen abbilden.

Die PDE-Debuggererweiterung (für den !pde.dse Befehl) ist verfügbar, indem sie die Datei "PDE*.zip " von OneDrive herunterladen. Fügen Sie die entsprechende x64- oder x86-Datei .dll aus dieser ZIP-Datei in das winext Verzeichnis Ihrer WinDbg-Installation ein, und arbeiten Sie dann !pde.dse an verstauten Ausnahmeabbildern.

Häufig gibt es mehrere verstaute Ausnahmen, wobei einige am Ende behandelt/ignoriert wurden. Am häufigsten ist die erste verstaute Ausnahme die interessante Ausnahme. In einigen Fällen kann die erste verstaute Ausnahme ein erneutes Auslösen der zweiten sein. Wenn also die zweite gestapelte Ausnahme tiefer in denselben Stapel wie die erste zeigt, kann die zweite Ausnahme die Ursache des Fehlers sein. Der fehlercode, der mit jeder verstauten Ausnahme angezeigt wird, ist ebenfalls hilfreich, da dadurch das HRESULT bereitgestellt wird, das dieser Ausnahme zugeordnet ist.

Ä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. Dies umfasst die Verwendung von Methoden oder Eigenschaften, die einen CoreDispatcher verwenden oder zurückgeben, z. B. die Eigenschaften DependencyObject.Dispatcher und CoreWindow.Dispatcher. 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 verwenden oder zurückgeben, z. B. die Eigenschaften DependencyObject.DispatcherQueue und Microsoft.UI.Xaml.Window.DispatcherQueue. 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 von 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 Folgendes 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());
    ...
}

Weitere Informationen