Шрифт:
MSG msg;
while(GetMessage(&msg, 0, 0, 0))
DispatchMessage(&msg);
Во втором случае окно, оконная процедура и очередь сообщений не используются.
Потоки с окном используются прежде всего при реализации интерфейса пользователя. Однако, эти потоки играют особую роль и в СОМ. Связанная с таким потоком очередь сообщений используется для синхронизации доступа к объектам, которые не рассчитаны на одновременный вызов их методов из различных потоков. Подробнее об этом рассказывается в следующем разделе.
Апартаменты
СОМ появился до того, как в Windows появилась многопоточность. В связи с этим, в рамках одного приложения могут взаимодействовать объекты, имеющие различный уровень знаний о потоках. Некоторые объекты ничего не знают о потоках. Если их погрузить в среду, в которой выполняется несколько потоков, то возможны проблемы. При параллельном вызове одного и того же метода такого объекта возможны, например, ранее описанные ошибки модификации глобальных и статических переменных. Другие объекты могут быть написаны с учетом возможности параллельного вызова их методов, например, с использованием критических секций. Такие объекты называются потоко-безопасными.
Апартаменты — это способ обеспечить совместную работу в рамках одного приложения объектов обоих типов. Тривиальное решение — запретить параллельное обращение ко всем объектам. Но это во многих случаях недопустимо. Например, система должна быстро реагировать на ввод данных от нескольких параллельно работающих пользователей. Следовательно, необходимо обеспечить для объекта каждого типа безопасную работу с максимально возможной производительностью.
Апартамент — это относительно сложное понятие. Но, тем не менее, следует затратить определенные усилия на то, чтобы разобраться в данном вопросе. Имеются экспертные оценки, в соответствии с которыми до 40 процентов ошибок при создании систем, основанных на технологии СОМ, вызваны недостаточным пониманием апартаментов.
Итак, начнем с иерархии процессов, апартаментов, потоков и объектов:
• В каждом процессе имеется один или несколько апартаментов.
• Каждый поток живет в определенном апартаменте.
• Каждый объект живет в определенном апартаменте.
Имеется три типа апартаментов:
• STA (Single-Threaded Apartment)
В одном процессе может иметься несколько апартаментов этого типа. Один из них (созданный первым) носит название главного STA. В STA живут объекты, не поддерживающие параллельный вызов своих методов. В каждом STA живет ровно один поток, причем это поток с окном. Именно этот поток выполняет все вызовы методов объектов, живущих в данном апартаменте.
• МТА (MultiThreaded Apartment)
В одном процессе может иметься только один МТА. В МТА живут объекты, поддерживающие параллельный вызов своих методов. С МТА связан пул потоков (рабочих потоков), из которого и выбирается новый поток для выполнения нового вызова метода объекта, живущего в данном апартаменте.
• NA (Neutral apartment)
В одном процессе может иметься только один NA. В NA живут объекты, поддерживающие параллельный вызов своих методов. Нет потоков, связанных с NA навечно. Любой поток из STA или МТА может временно покинуть свой апартамент и заняться выполнением некоторого метода некоторого объекта, живущего в NA.
Теперь рассмотрим процесс создания апартаментов, связь с ними потоков и объектов. Ограничимся случаем сервера, исполняющегося в процессе клиента.
Потоки создаются клиентом. Клиент имеет возможность связать данный поток с STA или МТА апартаментами. При этом либо создается новый апартамент, либо поток связывается с уже существующим апартаментом.
Если поток собирается работать с СОМ, он должен вызвать
CoInitialize(NULL), CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)
или
CoInitializeEx(NULL, COINIT_MULTITHREADED).
В первых двух случаях создается НОВЫЙ STA и данный поток связывается с этим STA. Первый из созданных STA объявляется главным. В третьем случае создается МТА, если он не был создан ранее. Данный поток связывается с МТА.
При создании апартамента в куче создается специальный объект апартамента, содержащий, в частности, такую информацию как идентификатор апартамента и его тип. Связь потока с апартаментом обеспечивается тем, что идентификатор апартамента сохраняется в локальной памяти потока (TLS — Thread Local Storage).
Создание объекта инициируется в определенном потоке, который уже приписан некоторому апартаменту. Но тип объекта, допустимость параллельного вызова его методов не определяется при создании объекта. Эта информация задается разработчиком класса и хранится в реестре системы.
Мы уже знаем, что если некоторый сервер предназначен для выполнения в процессе клиента, то для каждого его класса в реестре системы имеется раздел InProcServer32. В этом разделе имеется параметр по умолчанию, значением которого является путь к DLL, содержащей код данного сервера. В этом же разделе может иметься именованный параметр — ThreadingModel. Именно этот параметр определяет тип апартамента, в котором может жить экземпляр данного класса. В таблице 2.1 перечислены возможные значения данного параметра и допустимые типы апартаментов.