Работа с потоками и маршалинг (C++/CX)
В большинстве случаев доступ к экземплярам классов среда выполнения Windows, таких как стандартные объекты C++, можно получить из любого потока. Такие классы называются "гибкими". Однако небольшое количество классов среда выполнения Windows, которые отправляются с Windows, являются не гибкими, и их необходимо использовать больше, как COM-объекты, чем стандартные объекты C++. Для работы с негибкими классами не нужно быть специалистом по COM, однако нужно учитывать модель потоков классов и их механизмы маршалинга. В этой статье приведены общие сведения и инструкции по реализации редких сценариев, в которых приходится использовать экземпляры негибких классов.
Модель потоков и механизмы маршалинга
Класс среда выполнения Windows может поддерживать одновременный доступ к потокам различными способами, как указано двумя атрибутами, применяемыми к нему:
Атрибут
ThreadingModel
может иметь одно из следующих значений: STA, MTA или Both, которые определены в перечисленииThreadingModel
.Атрибут
MarshallingBehavior
может иметь одно из следующих значений: Agile, None или Standard, которые определены в перечисленииMarshallingType
.
Атрибут ThreadingModel
определяет, где загружается класс при активации: только в контексте потока пользовательского интерфейса (STA), только в контексте фонового потока (MTA) или в контексте потока, создающего объект (Both). Значения MarshallingBehavior
атрибутов ссылаются на то, как объект ведет себя в различных контекстах потоков. В большинстве случаев вам не нужно подробно понимать эти значения. Среди классов, предоставляемых API Windows, около 90 процентов имеют значения атрибутов ThreadingModel
=Both и MarshallingType
=Agile. Это означает, что они могут обрабатывать низкоуровневые сведения потоков прозрачно и эффективно. Если создать гибкий класс с помощью ключевого слова ref new
, его методы можно из основного потока приложения или из одного или нескольких рабочих потоков. Иначе говоря, гибкий класс можно использовать из любого места в коде, независимо от того, предоставляется ли он Windows или сторонними разработчиками. Вам не нужно беспокоиться о модели потоков класса или поведении маршалинга.
Использование компонентов среда выполнения Windows
При создании приложения универсальная платформа Windows вы можете взаимодействовать как с гибкими, так и не гибкими компонентами. При работе с негибкими компонентами может возникнуть следующее предупреждение.
Предупреждение компилятора C4451 при использовании негибких классов
По различным причинам некоторые классы не могут быть гибкими. Если вы обращаетесь к экземплярам негибких классов и из потока пользовательского интерфейса, и из фонового потока, необходимо уделить особое внимание тому, чтобы обеспечить правильное поведение во время выполнения. Компилятор Microsoft C++ выдает предупреждения при создании экземпляра класса, отличного от гибкой среды выполнения в приложении в глобальной области, или объявлять не гибкий тип как член класса в классе ссылок, который сам помечается как гибкий.
Из негибких классов проще всего работать с теми, у которых ThreadingModel
=Both и MarshallingType
=Standard. Эти классы можно сделать гибкими с помощью вспомогательного класса Agile<T>
. В следующем примере показано объявление негибкого объекта типа Windows::Security::Credentials::UI::CredentialPickerOptions^
и результирующее предупреждение компилятора.
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;
};
Выдается следующее предупреждение:
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
При добавлении ссылки (в области членов или глобальной области) на объект, имеющий поведение маршалинга "Standard", компилятор выдает предупреждение, рекомендующее заключить этот тип в оболочку Platform::Agile<T>
: Consider using 'Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions>' instead
Если воспользоваться типом Agile<T>
, класс можно будет использовать как любой другой гибкий класс. Используйте Platform::Agile<T>
в следующих ситуациях:
Негибкая переменная объявляется в глобальной области.
Негибкая переменная объявляется в области класса, и существует возможность того, что код использует указатель в другом подразделении без надлежащего маршалинга.
Если ни одно из этих условий не выполняется, можно пометить содержащий класс как негибкий. Другими словами, следует напрямую хранить негибкие объекты только в негибких классах, а также хранить негибкие объекты с помощью Platform::<Agile T> в гибких классах.
В следующем примере показано, как благодаря Agile<T>
можно пропустить это предупреждение без последствий.
#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;
};
Обратите внимание, что Agile
нельзя передавать в качестве возвращаемого значения или параметра в классе ссылки. Метод Agile<T>::Get()
возвращает дескриптор объекта (^), который можно передать через границу интерфейса ABI в открытом методе или свойстве.
При создании ссылки на класс in-proc среда выполнения Windows с маршалингом "Нет", компилятор выдает предупреждение C4451, но не предлагает использовать его.Platform::Agile<T>
Компилятор не может ничем помочь помимо этого предупреждения, поэтому вам необходимо самостоятельно обеспечить надлежащую работу класса и убедиться, что код вызывает компоненты однопотокового подразделения (STA) только из потока пользовательского интерфейса, а компоненты многопотокового подразделения (MTA) — только из фонового потока.
Разработка компонентов гибкой среда выполнения Windows
При определении класса ссылок в C++/CX он по умолчанию является гибким, т. е. имеет ThreadingModel
значение =И MarshallingType
=Agile. Если вы используете библиотеку шаблонов C++ среда выполнения Windows, вы можете сделать класс гибким, исходя из FtmBase
которого используется FreeThreadedMarshaller
. Создавая класс с атрибутами ThreadingModel
=Both или ThreadingModel
=MTA, убедитесь, что он является потокобезопасным.
Потоковую модель и поведение маршалинга класса ссылки можно изменять. Однако если внести изменения, которые делают класс негибким, необходимо четко понимать их последствия.
В следующем примере показано, как применять MarshalingBehavior
и ThreadingModel
атрибуты к классу среды выполнения в библиотеке классов среда выполнения Windows. Если приложение использует библиотеку DLL и в нем активируется объект класса ref new
с помощью ключевого слова MySTAClass
, этот объект активируется в однопотоковом подразделении и не поддерживает маршалинг.
using namespace Windows::Foundation::Metadata;
using namespace Platform;
[Threading(ThreadingModel::STA)]
[MarshalingBehavior(MarshalingType::None)]
public ref class MySTAClass
{
};
Незапечатанный класс должен иметь атрибуты маршалинга и потоковой модели, чтобы компилятор мог проверить, что производные классы имеют такие же значения этих атрибутов. Если эти параметры не заданы для класса явным образом, компилятор выдает ошибку и не выполняет компиляцию. Любой класс, производный от незапечатанного класса, выдает ошибку компилятора в каждом из следующих случаев:
Атрибуты
ThreadingModel
иMarshallingBehavior
не определены в производном классе.Значения атрибутов
ThreadingModel
иMarshallingBehavior
в производном классе не соответствуют аналогичным атрибутам базового класса.
Потоки и маршалинг сведений, необходимые стороннему компоненту среда выполнения Windows, указываются в сведениях о регистрации манифеста приложения для компонента. Мы рекомендуем сделать все компоненты среда выполнения Windows гибкими. Это гарантирует, что клиентский код сможет вызывать компонент из любого потока приложения, и улучшает производительность таких вызовов, поскольку они являются прямыми вызовами без маршалирования. Если создать класс таким образом, клиентскому коду не придется применять Platform::Agile<T>
, чтобы использовать этот класс.