Sdílet prostřednictvím


Migrace funkce zpracování vláken

Toto téma popisuje, jak migrovat kód pro práci s vlákny v aplikaci Universal Windows Platform (UWP) na sadu Windows App SDK.

Souhrn rozdílů mezi rozhraním API a/nebo funkcemi

Model vláken UWP je varianta modelu STA (Single-Threaded Apartment) označovaného jako Application STA (ASTA), který blokuje znovuvstup a pomáhá vyhnout se různým chybám způsobeným znovuvstupem a mrtvým smyčkám. Vlákno ASTA se také označuje jako vlákno uživatelského rozhraní.

Sada Windows App SDK používá standardní model podprocesů STA, který neposkytuje stejná opatření pro opětovný vstup.

Typ CoreDispatcher migruje na DispatcherQueue. A metoda CoreDispatcher.RunAsync migruje na DispatcherQueue.TryEnqueue.

C++/WinRT. Pokud používáte winrt::resume_foreground s CoreDispatcher, přemigrujte na použití DispatcherQueue.

Přechod z ASTA na STA modelu vláken

Další podrobnosti o modelu vláken ASTA najdete v blogovém příspěvku Co je tak speciální o aplikaci STA?.

Vzhledem k tomu, že model threadingu STA ve Windows App SDK neposkytuje stejné záruky ohledně prevence problémů s opakovaným vstupem, nemusí se váš kód chovat podle očekávání, pokud vaše aplikace UWP předpokládá chování ne-reentrantního modelu ASTA.

Jednou z věcí, na kterou je třeba dávat pozor, je opakovaný vstup do ovládacích prvků XAML (viz příklad v migraci ukázkové aplikace Editor fotografií z UWP na Windows App SDK (C++/WinRT)). A u některých chybových ukončení, jako jsou porušení přístupu, je zásobník volání přímého chybového ukončení obvykle správným zásobníkem, který se má použít. Pokud se ale jedná o odloženou výjimku a dojde k zhroucení s kódem výjimky: 0xc000027b, je potřeba více práce k získání správného zásobníku volání.

Uložené výjimky

Uložená výjimka ukládá možnou chybu, kterou použijeme později, pokud žádná část kódu výjimku nezpracuje. XAML někdy rozhodne, že chyba je okamžitě závažná, v takovém případě může být zásobník přímých chyb dobrý. Častěji se zásobník odvíjí dříve, než se zjistí, že je kritický. Další podrobnosti o zapouzdřených výjimkách naleznete v dílu Inside Show Zapouzdřená výjimka C000027B.

Pokud chcete zobrazit vnořenou smyčku zpráv nebo zhlédnout vyvolání konkrétní výjimky ovládacího prvku XAML, můžete získat další informace o chybovém ukončení načtením výpisu paměti v ladicím programu systému Windows (WinDbg) (viz Stažení nástrojů pro ladění pro Windows) a následným použitím !pde.dse k zobrazení uložených výjimek.

Rozšíření ladicího programu PDE (pro příkaz !pde.dse) je k dispozici stažením souboru PDE*.zip z OneDrivu. Vložte příslušný soubor x64 nebo x86 .dll z tohoto souboru ZIP do winext adresáře vaší instalace WinDbg a pak !pde.dse bude fungovat na výpisech stavu chybových ukončení.

Často se vyskytuje více uložených výjimek, přičemž některé na konci byly zpracovány nebo ignorovány. Nejčastěji je první uložená výjimka ta zajímavá. V některých případech může být první uložená výjimka opětovným vyvoláním druhé, takže pokud se druhá uložená výjimka objeví hlouběji ve stejném zásobníku jako první, může být druhá výjimka původem chyby. Kód chyby zobrazený s každou zaznamenou výjimkou je také cenný, protože poskytuje HRESULT přidružené k této výjimce.

Změna Windows.UI.Core.CoreDispatcher na Microsoft.UI.Dispatching.DispatcherQueue

Tato část platí, pokud používáte třídu Windows.UI.Core.CoreDispatcher v aplikaci pro UPW. Včetně použití jakýchkoli metod nebo vlastností, které přebírají nebo vrací CoreDispatcher, například vlastnosti DependencyObject.Dispatcher a CoreWindow.Dispatcher. Budete například volat DependencyObject.Dispatcher při načtení CoreDispatcher patřící Windows.UI.Xaml.Controls.Page.

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

Místo toho v aplikaci Windows App SDK budete muset použít třídu Microsoft.UI.Dispatching.DispatcherQueue. A odpovídající metody nebo vlastnosti, které přijímají nebo vracíDispatcherQueue, jako jsou vlastnosti DependencyObject.DispatcherQueue a Microsoft.UI.Xaml.Window.DispatcherQueue. Například budete volat DependencyObject.DispatcherQueue, když načtete DispatcherQueue, který patří stránce Microsoft.UI.Xaml.Controls.Page (většina objektů XAML jsou objekty typu DependencyObject).

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

Změna CoreDispatcher.RunAsync na DispatcherQueue.TryEnqueue

Tato část platí, pokud používáte metodu Windows.UI.Core.CoreDispatcher.RunAsync k naplánování úlohy, která se má spustit v hlavním vlákně uživatelského rozhraní (nebo ve vlákně přidruženém k určitému Windows.UI.CoreDispatcher).

// 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);
            });
    }
}

V aplikaci Windows App SDK použijte místo toho metodu Microsoft.UI.Dispatching.DispatcherQueue.TryEnqueue). Přidá do Microsoft.UI.Dispatching.DispatcherQueue úlohu, která se spustí ve vlákně přidruženém k DispatcherQueue.

// 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);
            });
    }
}

Přesunout winrt::resume_foreground (C++/WinRT)

Tato část platí, pokud použijete funkci winrt::resume_foreground v korutině v aplikaci C++/WinRT UWP.

V UWP je případ použití pro winrt::resume_foreground přepnutí provádění na vlákno popředí (toto vlákno popředí je často to, které je přidruženo k Windows.UI.Core.CoreDispatcher). Tady je příklad.

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

V aplikaci Windows App SDK:

Proto nejprve přidejte odkaz na balíček NuGet Microsoft.Windows.ImplementationLibrary.

Potom do pch.h v cílovém projektu přidejte následující include.

#include <wil/cppwinrt_helpers.h>

A pak postupujte podle níže uvedeného vzoru.

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

Viz také