Partilhar via


Migração da funcionalidade de encadeamento

Este tópico descreve como migrar o código de threading em um aplicativo da Plataforma Universal do Windows (UWP) para o SDK de Aplicativo do Windows.

Resumo das diferenças de API e/ou recursos

O modelo de threading da UWP é uma variação do modelo de apartamento de encadeamento único (STA) chamado Application STA (ASTA), que bloqueia a reentrância e ajuda a evitar vários bugs e deadlocks de reentrância. Um thread ASTA também é conhecido como thread da interface do usuário.

O SDK da aplicação Windows usa um modelo de encadeamento STA padrão, que não oferece as mesmas salvaguardas de reentrância.

O tipo de CoreDispatcher migra para DispatcherQueue. E o método CoreDispatcher.RunAsync é transferido para o método DispatcherQueue.TryEnqueue.

C++/WinRT. Se estiver a usar winrt::resume_foreground com CoreDispatcher, migre para usar DispatcherQueue.

Modelo de rosca ASTA para STA

Para obter mais detalhes sobre o modelo de threading ASTA, consulte a postagem do blog O que há de tão especial no Application STA?.

Como o modelo de threading STA do SDK de Aplicativo Windows não tem as mesmas garantias em relação à prevenção de problemas de reentrância, se seu aplicativo UWP assumir o comportamento de não reentrada do modelo de threading ASTA, seu código pode não se comportar conforme o esperado.

Uma coisa a ter em atenção é a reentrância em controlos XAML (consulte o exemplo em uma migração do Windows App SDK da aplicação de exemplo do editor de fotos UWP (C++/WinRT)). E para algumas falhas, como violações de acesso, a pilha de chamadas de falha direta geralmente é a pilha certa para usar. Mas, se for uma exceção armazenada que provoque uma falha — com código de exceção: 0xc000027b — então é necessário mais trabalho para obter a pilha de chamadas correta.

Exceções arquivadas

A exceção armazenada falha salvando um possível erro, e isso será usado posteriormente se nenhuma parte do código manipular a exceção. O XAML às vezes decide que o erro é fatal imediatamente, caso em que a pilha de crash direta pode ser útil. Mas com mais frequência a pilha foi desmontada antes de ser considerada fatal. Para mais detalhes sobre as exceções armazenadas, consulte o episódio do Inside Show, intitulado "Stowed Exception C000027B".

Para falhas de exceção armazenadas (para ver uma bomba de mensagem aninhada ou para ver a exceção específica do controle XAML sendo lançada), você pode obter mais informações sobre a falha carregando um despejo de memória no Depurador do Windows (WinDbg) (consulte Baixar ferramentas de depuração para Windows) e, em seguida, usando !pde.dse para despejar as exceções armazenadas.

A extensão do depurador PDE (para o comando ) está disponível descarregando o arquivo PDE*.zip do OneDrive. Coloque o x64 ou x86 .dll apropriado desse arquivo zip no diretório da instalação do WinDbg e, em seguidawinext, funcionará em despejos !pde.dse de falha de exceção armazenados.

Freqüentemente haverá várias exceções armazenadas, com algumas no final que foram manipuladas/ignoradas. Com mais frequência, a primeira exceção armazenada é a interessante. Em alguns casos, a primeira exceção armazenada pode ser um novo lançamento da segunda, portanto, se a segunda exceção armazenada aparecer mais abaixo na mesma pilha que a primeira, então a segunda exceção pode ser a origem do erro. O código de erro mostrado com cada exceção armazenada também é valioso, pois fornece o HRESULT associado a essa exceção.

Altere Windows.UI.Core.CoreDispatcher para Microsoft.UI.Dispatching.DispatcherQueue

Esta seção se aplica se você estiver usando a classe Windows.UI.Core.CoreDispatcher em seu aplicativo UWP. Isso inclui o uso de quaisquer métodos ou propriedades que aceitam ou devolvem um CoreDispatcher, como as propriedades DependencyObject.Dispatcher e CoreWindow.Dispatcher. Por exemplo, você estará chamando DependencyObject.Dispatcher quando recuperar o CoreDispatcher pertencente a um 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())
{
    ...
}

Em vez disso, na sua aplicação SDK do Windows, precisará usar a classe Microsoft.UI.Dispatching.DispatcherQueue. E os métodos ou propriedades correspondentes que recebem ou retornam um DispatcherQueue, tal como as propriedades DependencyObject.DispatcherQueue e Microsoft.UI.Xaml.Window.DispatcherQueue. Por exemplo, irá chamar DependencyObject.DispatcherQueue quando recuperar a DispatcherQueue de uma Microsoft.UI.Xaml.Controls.Page (a maioria dos objetos XAML são 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())
{
    ...
}

Altere CoreDispatcher.RunAsync para DispatcherQueue.TryEnqueue

Esta seção se aplica se você estiver usando o método Windows.UI.Core.CoreDispatcher.RunAsync para agendar uma tarefa para ser executada no thread principal da interface do usuário (ou no thread associado a umespecífico Windows.UI.Core.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);
            });
    }
}

Na sua aplicação SDK do Windows, em vez disso, use o método Microsoft.UI.Dispatching.DispatcherQueue.TryEnqueue). Ele adiciona ao Microsoft.UI.Dispatching.DispatcherQueue uma tarefa que será executada no thread associado ao 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);
            });
    }
}

Migrar winrt::resume_foreground (C++/WinRT)

Esta seção se aplica se você usar a função winrt::resume_foreground em uma corrotina no seu aplicativo UWP C++/WinRT.

Na UWP, a utilização do winrt::resume_foreground serve para alternar a execução para um thread de primeiro plano (esse thread de primeiro plano é frequentemente aquele associado a um Windows.UI.Core.CoreDispatcher). Aqui está um exemplo disso.

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

Na sua aplicação SDK de Aplicações Windows:

Portanto, primeiro adicione uma referência ao pacote NuGet Microsoft.Windows.ImplementationLibrary .

Em seguida, adicione o seguinte include ao pch.h no projeto de destino.

#include <wil/cppwinrt_helpers.h>

E, em seguida, siga o padrão mostrado abaixo.

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

Ver também