Partilhar via


Threading e Marshaling (C++/CX)

Na grande maioria dos casos, instâncias de classes do Tempo de Execução do Windows, como objetos C++ padrão, podem ser acessadas de qualquer thread. Tais classes são referidas como "ágeis". No entanto, um pequeno número de classes do Tempo de Execução do Windows fornecidas com o Windows não são ágeis e devem ser consumidas mais como objetos COM do que objetos C++ padrão. Você não precisa ser um especialista em COM para usar classes não ágeis, mas precisa levar em consideração o modelo de threading da classe e seu comportamento de marshaling. Este artigo fornece informações básicas e orientações para os raros cenários em que você precisa consumir uma instância de uma classe não ágil.

Modelo de threading e comportamento de marshaling

Uma classe do Tempo de Execução do Windows pode oferecer suporte ao acesso simultâneo a threads de várias maneiras, conforme indicado por dois atributos que são aplicados a ela:

  • ThreadingModel pode ter um dos valores — STA, MTA ou Ambos, conforme definido pela ThreadingModel enumeração.

  • MarshallingBehavior pode ter um dos valores — Agile, Nenhum ou Padrão, conforme definido pela MarshallingType enumeração.

O ThreadingModel atributo especifica onde a classe é carregada quando ativada: somente em um contexto de thread de interface do usuário (STA), somente em um contexto de thread em segundo plano (MTA) ou no contexto do thread que cria o objeto (Both). Os MarshallingBehavior valores de atributo referem-se a como o objeto se comporta nos vários contextos de threading, na maioria dos casos, você não precisa entender esses valores em detalhes. Das classes fornecidas pela API do Windows, cerca de 90% têm ThreadingModel=Ambos e MarshallingType=Agile. Isso significa que eles podem lidar com detalhes de threading de baixo nível de forma transparente e eficiente. Quando você usa ref new para criar uma classe "ágil", você pode chamar métodos nela a partir do thread principal do aplicativo ou de um ou mais threads de trabalho. Em outras palavras, você pode usar uma classe ágil — não importa se ela é fornecida pelo Windows ou por terceiros — de qualquer lugar em seu código. Você não precisa se preocupar com o modelo de threading da classe ou com o comportamento de marshaling.

Consumindo componentes do Tempo de Execução do Windows

Ao criar um aplicativo da Plataforma Universal do Windows, você pode interagir com componentes ágeis e não ágeis. Quando você interage com componentes não ágeis, você pode encontrar o seguinte aviso.

Aviso do compilador C4451 ao consumir classes não ágeis

Por vários motivos, algumas aulas não conseguem ser ágeis. Se você estiver acessando instâncias de classes não ágeis a partir de um thread de interface do usuário e um thread em segundo plano, tome cuidado extra para garantir o comportamento correto em tempo de execução. O compilador Microsoft C++ emite avisos quando você instancia uma classe de tempo de execução não ágil em seu aplicativo no escopo global ou declara um tipo não ágil como um membro de classe em uma classe ref que está marcada como ágil.

Das classes não ágeis, as mais fáceis de lidar são aquelas que têm ThreadingModel=Ambos e MarshallingType=Padrão. Você pode tornar essas classes ágeis apenas usando a Agile<T> classe auxiliar. O exemplo a seguir mostra uma declaração de um objeto não ágil do tipo Windows::Security::Credentials::UI::CredentialPickerOptions^, e o aviso do compilador que é emitido como resultado.

ref class MyOptions
    {
    public:
        property Windows::Security::Credentials::UI::CredentialPickerOptions^ Options

        {
            Windows::Security::Credentials::UI::CredentialPickerOptions^ get()
            {
                return _myOptions;
            }
        }
    private:
        Windows::Security::Credentials::UI::CredentialPickerOptions^ _myOptions;
    };

Aqui está o aviso emitido:

Warning 1 warning C4451: 'Platform::Agile<T>::_object' : Usage of ref class 'Windows::Security::Credentials::UI::CredentialPickerOptions' inside this context can lead to invalid marshaling of object across contexts. Consider using 'Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions>' instead

Quando você adiciona uma referência — no escopo do membro ou no escopo global — a um objeto que tem um comportamento de empacotamento de "Padrão", o compilador emite um aviso que o aconselha a encapsular o tipo : Platform::Agile<T>Consider using 'Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions>' instead Se você usar Agile<T>o , você pode consumir a classe como qualquer outra classe ágil. Utilização Platform::Agile<T> nestas circunstâncias:

  • A variável não-ágil é declarada no âmbito global.

  • A variável não ágil é declarada no escopo da classe e há uma chance de que o consumo de código possa contrabandear o ponteiro — ou seja, usá-lo em um apartamento diferente sem o marshaling correto.

Se nenhuma dessas condições se aplicar, você poderá marcar a classe que contém como não ágil. Em outras palavras, você deve manter diretamente objetos não ágeis apenas em classes não ágeis e manter objetos não ágeis via Platform::Agile<T> em classes ágeis.

O exemplo a seguir mostra como usar Agile<T> para que você possa ignorar o aviso com segurança.

#include <agile.h>
ref class MyOptions
    {
    public:
        property Windows::Security::Credentials::UI::CredentialPickerOptions^ Options

        {
            Windows::Security::Credentials::UI::CredentialPickerOptions^ get()
            {
                return m_myOptions.Get();
            }
        }
    private:
        Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions^> m_myOptions;

    };

Observe que Agile não pode ser passado como um valor de retorno ou parâmetro em uma classe ref. O Agile<T>::Get() método retorna um identificador para objeto (^) que você pode passar através da interface binária do aplicativo (ABI) em um método público ou propriedade.

Quando você cria uma referência a uma classe do Tempo de Execução do Windows in-proc que tem um comportamento de empacotamento de "Nenhum", o compilador emite um aviso C4451, mas não sugere que você considere o uso Platform::Agile<T>do . O compilador não pode oferecer nenhuma ajuda além deste aviso, portanto, é sua responsabilidade usar a classe corretamente e garantir que seu código chame componentes STA somente do thread da interface do usuário e componentes MTA somente de um thread em segundo plano.

Criação de componentes ágeis do Tempo de Execução do Windows

Quando você define uma classe ref em C++/CX, ela é ágil por padrão, ou seja, tem ThreadingModel=Both e MarshallingType=Agile. Se você estiver usando a Biblioteca de Modelos C++ do Tempo de Execução do Windows, poderá tornar sua classe ágil derivando do FtmBase, que usa o FreeThreadedMarshaller. Se você criar uma classe que tenha ThreadingModel=Both ou ThreadingModel=MTA, certifique-se de que a classe é thread-safe.

Você pode modificar o modelo de threading e o comportamento de empacotamento de uma classe ref. No entanto, se você fizer alterações que tornem a classe não ágil, deverá entender as implicações associadas a essas alterações.

O exemplo a seguir mostra como aplicar MarshalingBehavior e ThreadingModel atributos a uma classe de tempo de execução em uma biblioteca de classes do Tempo de Execução do Windows. Quando um aplicativo usa a DLL e usa a ref new palavra-chave para ativar um MySTAClass objeto de classe, o objeto é ativado em um apartamento de thread único e não oferece suporte a marshaling.

using namespace Windows::Foundation::Metadata;
using namespace Platform;

[Threading(ThreadingModel::STA)]
[MarshalingBehavior(MarshalingType::None)]
public ref class MySTAClass
{
};

Uma classe sem lacre deve ter configurações de atributo de empacotamento e threading para que o compilador possa verificar se as classes derivadas têm o mesmo valor para esses atributos. Se a classe não tiver as configurações definidas explicitamente, o compilador gerará um erro e não conseguirá compilar. Qualquer classe derivada de uma unsealedclass gera um erro de compilador em qualquer um destes casos:

  • Os ThreadingModel atributos e MarshallingBehavior não são definidos na classe derivada.

  • Os valores dos ThreadingModel e MarshallingBehavior atributos na classe derivada não correspondem aos da classe base.

As informações de threading e marshaling exigidas por um componente do Tempo de Execução do Windows de terceiros são especificadas nas informações de registro do manifesto do aplicativo para o componente. Recomendamos que você torne todos os componentes do Tempo de Execução do Windows ágeis. Isso garante que o código do cliente possa chamar seu componente de qualquer thread no aplicativo e melhora o desempenho dessas chamadas porque são chamadas diretas que não têm marshaling. Se você criar sua classe dessa maneira, o código do cliente não precisará ser usado Platform::Agile<T> para consumir sua classe.

Ver também

ThreadingModel
MarshallingComportamento