Share via


Migration von Threadingfunktionen

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

Zusammenfassung der API- und/oder Funktionsunterschiede

Das Threadingmodell von UWP ist eine Variante des Single-Threaded-Apartment-Modells (STA) mit dem Namen Application STA (ASTA), das die Erneuteintretung blockiert und verschiedene Fehler und Deadlocks verhindert. Ein ASTA-Thread wird auch als UI-Thread bezeichnet.

Die Windows App SDK verwendet ein standardmäßiges STA-Threadingmodell, das nicht die gleichen Reentrancy-Schutzmaßnahmen bietet.

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

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

ASTA zu STA-Threadingmodell

Weitere Details zum ASTA-Threadingmodell finden Sie im Blogbeitrag Was ist das Besondere an der Anwendungs-STA?.

Da das STA-Threadingmodell des Windows App SDK nicht über die gleichen Sicherheiten verfügt, um Probleme mit erneuter Antranz zu verhindern, verhält sich Ihr Code möglicherweise nicht wie erwartet, wenn Ihre UWP-App das Verhalten des ASTA-Threadingmodells ohne Erneuteinsteiger annimmt.

Eine Sache, für die Sie watch müssen, ist die Erneuteintrakation in XAML-Steuerelemente (siehe das Beispiel in A Windows App SDK Migration der UWP-Foto-Editor-Beispiel-App (C++/WinRT)). Und für einige Abstürze, z. B. Zugriffsverletzungen, ist der direkte Absturzaufrufstapel normalerweise der richtige Stapel, der verwendet werden kann. Wenn es sich jedoch um einen verstauten Ausnahmeabsturz handelt – mit Ausnahmecode: 0xc000027b –, ist mehr Arbeit erforderlich, um den richtigen Aufrufstapel zu erhalten.

Verstaute Ausnahmen

Bei Abstürzen von stowed-Ausnahmen wird ein möglicher Fehler gespeichert, der später verwendet wird, wenn kein Teil des Codes die Ausnahme verarbeitet. XAML entscheidet manchmal, dass der Fehler sofort fatal ist, in diesem Fall kann der direkte Absturzstapel gut sein. Aber häufiger wurde der Stapel entwickelt, bevor er als tödlich eingestuft wurde. Weitere Informationen zu verstauten Ausnahmen finden Sie in der Inside Show-Episode Stowed Exception C000027B.

Bei verstauten Ausnahmeabstürzen (um eine geschachtelte Nachrichtenpumpe anzuzeigen oder die spezifische Ausnahme des XAML-Steuerelements zu sehen, die ausgelöst wird), können Sie weitere Informationen zum Absturz erhalten, indem Sie ein Absturzabbild in den Windows-Debugger (WinDbg) laden (siehe Herunterladen von Debugtools für Windows) und dann !pde.dse verwenden, um die verstauten Ausnahmen abzubilden.

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

Häufig gibt es mehrere verstaute Ausnahmen, wobei einige am Ende behandelt/ignoriert wurden. Am häufigsten ist die erste verstaute Ausnahme die interessante. In einigen Fällen kann die erste verstaute Ausnahme ein erneuter Wurf der zweiten sein. Wenn also die zweite verstaute Ausnahme tiefer in den selben Stapel als die erste angezeigt wird, kann die zweite Ausnahme den Ursprung des Fehlers sein. Der Fehlercode, der mit jeder verstauten Ausnahme angezeigt wird, ist ebenfalls nützlich, da er das dieser Ausnahme zugeordnete HRESULT bereitstellt.

Ändern Sie 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 übernehmen 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 einer 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 übernehmen oder zurückgeben, z. B. die Eigenschaften DependencyObject.DispatcherQueue und Microsoft.UI.Xaml.Window.DispatcherQueue . Sie rufen beispielsweise 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 die Ausführung eines Tasks im Standard UI-Thread (oder im Thread, der einem bestimmten Windows.UI.Core.CoreDispatcher zugeordnet ist) zu planen.

// 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 Microsoft.UI.Dispatching.DispatcherQueue.TryEnqueue)-Methode. Es fügt der Microsoft.UI.Dispatching.DispatcherQueue eine Aufgabe hinzu, die für den Thread ausgeführt wird, der dem 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, der einem Windows.UI.Core.CoreDispatcher zugeordnet ist). Hier sehen Sie 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 daher zunächst einen Verweis auf das NuGet-Paket Microsoft.Windows.ImplementationLibrary hinzu.

Fügen Sie dann dem Zielprojekt den folgenden Include hinzu pch.h .

#include <wil/cppwinrt_helpers.h>

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());
    ...
}