Threading e marshaling (C++/CX)

Na grande maioria dos casos, as instâncias das classes do Windows Runtime, como os objetos C++ padrão, podem ser acessadas de qualquer thread. Essas classes são chamadas de "agile". No entanto, um pequeno número das classes do Windows Runtime fornecidas com o Windows não é agile e deve ser mais consumidas como objetos COM do que como objetos C++ padrão. Não é necessário ser um especialista em COM para usar classes não agile, mas é preciso levar em consideração o modelo de threading da classe e seu comportamento de marshaling. Este artigo fornece informações básicas e orientação para os cenários raros em que é necessário consumir uma instância de uma classe não agile.

Modelo de threading e comportamento de marshaling

Uma classe do Windows Runtime pode oferecer suporte ao acesso de thread simultâneo de várias maneiras, conforme indicado pelos dois atributos aplicados a ela:

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

  • O atributoMarshallingBehavior pode ter um dos valores — Agile, None ou Standard, conforme definido pela enumeração MarshallingType .

O atributo ThreadingModel especifica onde a classe é carregada quando ativada: somente em um contexto de thread da interface de usuário (STA), somente em um contexto de thread de segundo plano (MTA) ou no contexto da 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=Both e MarshallingType=Agile. Isso significa que podem manipular detalhes de threading de baixo nível de modo transparente e eficiente. Quando você usa ref new para criar uma classe "agile", pode chamar métodos nela a partir do thread de aplicativo principal ou de um ou mais threads de trabalho. Em outras palavras, você pode usar uma classe agile — quer ela tenha sido fornecida pelo Windows ou por um terceiro — de qualquer lugar no código. Você não precisa se preocupar com o modelo de threading da classe ou com o comportamento de marshaling.

Consumindo componentes do Windows Runtime

Ao criar um aplicativo da Plataforma Universal do Windows, você pode interagir com componentes agile e não agile. Quando você interage com componentes não agile, pode receber o aviso a seguir.

Aviso do compilador C4451 durante o consumo de classes não agile

Por várias razões, algumas classes não podem ser agile. Se você estiver acessando instâncias de classes não agile, tanto de um thread de interface de usuário quanto de um thread de segundo plano, tome ainda mais cuidado para verificar o comportamento correto em tempo de execução. O compilador do Microsoft C++ emite avisos quando você instancia uma classe de tempo de execução não agile no seu aplicativo no escopo global ou declare um tipo não agile como um membro da classe em uma classe ref que é marcada como agile.

Das classes não agile, a maneira mais fácil de lidar com isso são aqueles que têm ThreadingModel=Both e MarshallingType=Standard. Você pode tornar essas classes agile usando simplesmente a classe auxiliar Agile<T> . O exemplo a seguir mostra uma declaração de um objeto não agile do tipo Windows::Security::Credentials::UI::CredentialPickerOptions^e o aviso do compilador emitido em virtude disso.

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 que é 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 marshaling de "Standard", o compilador emite um aviso que o aconselha a encapsular o tipo em Platform::Agile<T>: Consider using 'Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions>' instead Se você usar Agile<T>, poderá consumir a classe como qualquer outra classe agile. Use Platform::Agile<T> nessas circunstâncias:

  • A variável não agile é declarada no escopo global.

  • A variável não agile é declarada no escopo da classe e há uma possibilidade de o código de consumo ocultar o ponteiro — ou seja, usá-lo em um apartment diferente, sem o marshaling correto.

Caso nenhuma dessas condições se aplique, você poderá marcar a classe continente como não agile. Em outras palavras, você deve manter, diretamente, apenas objetos não agile em classes não agile e os não agile, via Platform::Agile<T>, em classes agile.

O exemplo a seguir mostra como você pode utilizar o Agile<T> para poder 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 parâmetro ou um valor de retorno em uma classe ref. O método Agile<T>::Get() retorna um handle-to-object (^) que você pode passar na ABI (interface binária do aplicativo) em um método ou propriedade pública.

Ao criar uma referência para uma classe do Windows Runtime no in-proc que tem um comportamento marshaling de "None", o compilador emite o aviso C4451, mas não sugere que você considere o uso de Platform::Agile<T>. O compilador não pode oferecer nenhuma ajuda além desse aviso, portanto, é sua responsabilidade usar a classe corretamente e verificar se seu código chama componentes do STA somente do thread da interface do usuário e componentes MTA somente de um thread de segundo plano.

Criação de componentes agile do Windows Runtime

Ao definir uma classe ref no C++/CX, ela é agile por padrão, ou seja, tem ThreadingModel=Both e MarshallingType=Agile. Se você estiver usando a Biblioteca de Modelos C++ do Windows Runtime, poderá tornar sua classe agile derivando de FtmBase, que usa o FreeThreadedMarshaller. Se você criar uma classe que tenha ThreadingModel=Both ou ThreadingModel=MTA, verifique se a classe é thread-safe.

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

O exemplo a seguir mostra como aplicar os atributos MarshalingBehavior e ThreadingModel a uma classe de runtime em uma biblioteca de classes do Windows Runtime. Quando um aplicativo usa a DLL e a palavra-chave ref new para ativar um objeto de classe MySTAClass , o objeto é ativado em um single-threaded apartment e não oferece suporte para marshaling.

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

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

Uma classe não lacrada deve ter configurações de atributo marshaling 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 explicitamente definidas, o compilador gerará um erro e não compilará. Qualquer classe derivada de uma classe não lacrada gera um erro do compilador em qualquer um destes casos:

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

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

As informações de threading e marshaling necessárias para um componente do Windows Runtime de terceiros são especificadas nas informações de registro de manifesto do aplicativo do componente. Recomendamos que você torne todos os seus componentes agile do Windows Runtime. Isso assegura que o código de cliente possa chamar seu componente de qualquer thread no aplicativo e aprimora o desempenho dessas chamadas, pois elas são chamadas diretas que não têm marshaling. Se você criar uma classe dessa maneira, o código de cliente não terá que usar Platform::Agile<T> para consumir sua classe.

Confira também

ThreadingModel
MarshallingBehavior