Wątkowość i marshaling (C++/CX)

W przeważającej większości przypadków wystąpienia klas środowisko wykonawcze systemu Windows, takich jak standardowe obiekty języka C++, mogą być dostępne z dowolnego wątku. Takie klasy są określane jako "agile". Jednak niewielka liczba klas środowisko wykonawcze systemu Windows, które są dostarczane z systemem Windows, są niezwinne i muszą być używane bardziej jak obiekty COM niż standardowe obiekty C++. Nie musisz być ekspertem COM, aby korzystać z klas niezwinnych, ale należy wziąć pod uwagę model wątków klasy i jego zachowanie marshalingu. Ten artykuł zawiera podstawowe informacje i wskazówki dotyczące tych rzadkich scenariuszy, w których należy użyć wystąpienia klasy niezwinnej.

Model wątkowy i zachowanie marshalingu

Klasa środowisko wykonawcze systemu Windows może obsługiwać współbieżny dostęp do wątków na różne sposoby, co wskazuje na dwa atrybuty, które są do niego stosowane:

  • ThreadingModel Atrybut może mieć jedną z wartości — STA, MTA lub Oba, zgodnie z definicją wyliczenia ThreadingModel .

  • MarshallingBehavior Atrybut może mieć jedną z wartości — Agile, None lub Standard zgodnie z definicją wyliczenia MarshallingType .

Atrybut ThreadingModel określa, gdzie klasa jest ładowana po aktywowaniu: tylko w kontekście wątku interfejsu użytkownika (STA), tylko w kontekście wątku tła (MTA) lub w kontekście wątku, który tworzy obiekt (Oba). Wartości MarshallingBehavior atrybutów odnoszą się do tego, jak obiekt zachowuje się w różnych kontekstach wątków. W większości przypadków nie trzeba szczegółowo rozumieć tych wartości. Spośród klas udostępnianych przez interfejs API systemu Windows około 90 procent ma ThreadingModel=Zarówno, jak i MarshallingType=Agile. Oznacza to, że mogą obsługiwać szczegóły wątków niskiego poziomu w sposób niewidoczny i wydajny. Gdy używasz ref new metody do utworzenia klasy "agile", możesz wywołać metody z głównego wątku aplikacji lub z co najmniej jednego wątku roboczego. Innymi słowy, możesz użyć klasy agile — niezależnie od tego, czy jest ona dostarczana przez system Windows, czy przez inną firmę — z dowolnego miejsca w kodzie. Nie musisz zajmować się modelem wątkowym klasy ani zachowaniem marshalingu.

Korzystanie ze składników środowisko wykonawcze systemu Windows

Podczas tworzenia aplikacji platforma uniwersalna systemu Windows możesz korzystać zarówno ze składników agile, jak i niezwinnych. W przypadku interakcji ze składnikami niezwinnymi może wystąpić następujące ostrzeżenie.

Ostrzeżenie kompilatora C4451 podczas korzystania z klas niezwinnych

Z różnych powodów niektóre klasy nie mogą być zwinne. Jeśli uzyskujesz dostęp do wystąpień klas niezwinnych zarówno z wątku interfejsu użytkownika, jak i wątku w tle, należy zachować szczególną ostrożność, aby zapewnić prawidłowe zachowanie w czasie wykonywania. Kompilator Microsoft C++ zgłasza ostrzeżenia podczas tworzenia wystąpienia klasy niezwinnej w czasie wykonywania w aplikacji w zakresie globalnym lub deklarowania typu niezwinnego jako elementu członkowskiego klasy w klasie ref, która jest oznaczona jako zwinna.

Z klas niezwinnych najłatwiej radzić sobie z tymi, które mają ThreadingModel=Zarówno, jak i MarshallingType=Standard. Te klasy można zwinić tylko za pomocą klasy pomocniczej Agile<T> . W poniższym przykładzie pokazano deklarację obiektu typu Windows::Security::Credentials::UI::CredentialPickerOptions^non-agile i ostrzeżenie kompilatora, które jest wystawiane w wyniku.

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

Oto ostrzeżenie, które zostało wydane:

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

Podczas dodawania odwołania — w zakresie składowym lub zakresie globalnym — do obiektu, który ma zachowanie marshalingowe "Standard", kompilator wydaje ostrzeżenie, które zaleca zawijanie typu w Platform::Agile<T>elemencie : Consider using 'Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions>' instead Jeśli używasz Agile<T>metody , możesz użyć klasy, takiej jak dowolna inna klasa agile. Użyj Platform::Agile<T> w następujących okolicznościach:

  • Zmienna niezwinna jest deklarowana w zakresie globalnym.

  • Zmienna niezwinna jest deklarowana w zakresie klasy i istnieje prawdopodobieństwo, że użycie kodu może przemycić wskaźnik — czyli użycie go w innym mieszkaniu bez poprawnego marshalingu.

Jeśli żaden z tych warunków nie ma zastosowania, możesz oznaczyć klasę zawierającą jako niezwinną. Innymi słowy, należy bezpośrednio przechowywać obiekty niezwinne tylko w klasach niezwinnych i przechowywać obiekty niezwinne za pomocą metody Platform::Agile<T> w klasach agile.

W poniższym przykładzie pokazano, jak używać Agile<T> polecenia , aby można było bezpiecznie zignorować ostrzeżenie.

#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;

    };

Zwróć uwagę, że Agile nie można przekazać jako wartości zwracanej ani parametru w klasie ref. Metoda Agile<T>::Get() zwraca metodę handle-to-object (^), którą można przekazać przez interfejs binarny aplikacji (ABI) w publicznej metodzie lub właściwości.

Podczas tworzenia odwołania do klasy środowisko wykonawcze systemu Windows in-proc, która ma zachowanie marshalingowe "None", kompilator wydaje ostrzeżenie C4451, ale nie sugeruje, aby rozważyć użycie polecenia Platform::Agile<T>. Kompilator nie może zaoferować żadnej pomocy poza tym ostrzeżeniem, dlatego twoim zadaniem jest prawidłowe użycie klasy i upewnienie się, że kod wywołuje składniki STA tylko z wątku interfejsu użytkownika i składników MTA tylko z wątku w tle.

Tworzenie elastycznych składników środowisko wykonawcze systemu Windows

Podczas definiowania klasy ref w języku C++/CX jest ona domyślnie elastyczna — ma ona ThreadingModelwartość =Zarówno, jak i MarshallingType=Agile. Jeśli używasz biblioteki szablonów języka środowisko wykonawcze systemu Windows C++, możesz zwinnieć klasę, korzystając z FtmBaseklasy , która używa FreeThreadedMarshallerklasy . Jeśli tworzysz klasę z ThreadingModelwartością =Both lub ThreadingModel=MTA, upewnij się, że klasa jest bezpieczna wątkowo.

Można zmodyfikować model wątków i marshaling zachowanie klasy ref. Jeśli jednak wprowadzisz zmiany, które renderują klasę niezwinną, musisz zrozumieć implikacje, które są skojarzone z tymi zmianami.

W poniższym przykładzie pokazano, jak zastosować MarshalingBehavior atrybuty i ThreadingModel do klasy środowiska uruchomieniowego w bibliotece klas środowisko wykonawcze systemu Windows. Gdy aplikacja używa biblioteki DLL i używa ref new słowa kluczowego MySTAClass do aktywowania obiektu klasy, obiekt jest aktywowany w jednym wątku i nie obsługuje marshalingu.

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

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

Niezaziemowana klasa musi mieć ustawienia atrybutów marshalingu i wątkowania, aby kompilator mógł sprawdzić, czy klasy pochodne mają taką samą wartość dla tych atrybutów. Jeśli klasa nie ma jawnie ustawionych ustawień, kompilator generuje błąd i nie można skompilować. Każda klasa pochodząca z niesealedclass generuje błąd kompilatora w jednym z następujących przypadków:

  • Atrybuty ThreadingModel i MarshallingBehavior nie są zdefiniowane w klasie pochodnej.

  • Wartości atrybutów ThreadingModel i MarshallingBehavior w klasie pochodnej nie są zgodne z wartościami w klasie bazowej.

Informacje wątkowe i marshalingowe wymagane przez składnik środowisko wykonawcze systemu Windows innej firmy są określone w informacjach dotyczących rejestracji manifestu aplikacji dla składnika. Zalecamy zwinność wszystkich składników środowisko wykonawcze systemu Windows. Dzięki temu kod klienta może wywołać składnik z dowolnego wątku w aplikacji i poprawi wydajność tych wywołań, ponieważ są to bezpośrednie wywołania, które nie mają marshalingu. Jeśli w ten sposób utworzysz klasę, kod klienta nie musi używać Platform::Agile<T> klasy.

Zobacz też

Threadingmodel
MarshallingBehavior