«Машины должны работать. Люди должны думать»
56. Апартаменты и многопоточность в СОМ
Processes and Threads – этот раздел взят частично из MSDN (раздел Platform SDK\COM and ActiveX Object Services\COM\ Processes and Threads). См. также стр. 256 у Оберга.
Процесс представляет собой совокупность виртуальной памяти, программного кода, данных и системных ресурсов, а поток – это программный код, выполняющийся последовательно внутри процесса. Процессор выполняет потоки, а не процессы, так что любое 32-разрядное приложение имеет, по крайней мере, один процесс и один поток. До появления многопоточности все приложения разрабатывались для выполнения одного потока.
Процессы взаимодействуют друг с другом посредством механизма RPC – удаленного вызова процедур. Для вызываемого процесса безразлично, пришел ли вызов от процесса, исполняемого на другом компьютере или на том же самом.
Несмотря на то, что в COM используются три модели потоков, некоторые положения применимы к потокам и процессам в целом. Любой процесс всегда имеет по крайней мере один поток, называемый первичным, и может иметь любое количество дополнительных потоков. Когда поток начинает исполняться, он продолжается до завершения или до того момента, когда он будет прерван потоком с более высоким приоритетом (пользователем или планировщиком ядра ОС). Каждый поток может исполнять различные секции программного кода или несколько потоков могут исполнять один и тот же фрагмент кода. Каждый поток процесса использует (совместно с другими потоками) глобальные переменные и ресурсы своего процесса.
Планировщик Windows NT определяет, когда и как часто надо выполнять поток в соответствии с комбинацией класса приоритета процесса и приоритета потока. Класс приоритета процесса можно изменить с помощью функции Win32 SetPriorityClass, а приоритет потока – с помощью SetThreadPriority.
Многопоточные приложения должны избегать двух проблем: тупиков (deadlocks) и гонок (races). Тупик случается тогда, когда два или более потока ожидают друг друга. Ситуация гонок заключается в том, когда один поток завершается раньше другого, от которого он зависит.
COM помогает избежать тупиков в вызовах методов объектов путем предоставления функций, специально разработанных для того, чтобы избежать возникновения гонок во внепроцессных серверах.
Чтобы понять архитектуру потоков COM, можно себе представить, что все объекты COM в процессе поделены на группы, называемые апартаментами. Поскольку COM объект живет в точности в одном апартаменте, его методы могут быть непосредственно вызваны только из потока, который принадлежит этому апартаменту. Любой другой поток, который хочет вызвать объект, должен это делать через заместителя (proxy).
Апартаменты бывают двух типов: однопоточные (STA – Single-threaded Apartments) и многопоточные (MTA – Multi-threaded Apartments).
Однопоточные апартаменты.
В STA каждый поток, который использует OLE, находится в отдельном апартаменте и COM синхронизирует все входящие вызовы с помощью очереди оконных сообщений. Для этого COM неявно создает окно и при вызове любого метода объекта посылает этому окну сообщение с помощью функции PostMessage. (Функция PostMessage создает MSG структуру для сообщения и копирует ее в очередь сообщений окна.) Процесс с единственным потоком выполнения может служить иллюстрацией этой модели.
STA апартаменты имеют в точности один поток, так что все объекты, которые «живут» в однопоточном апартаменте, могут получать вызовы методов только от одного потока, который принадлежит этому апартаменту.
Главным достоинством апартамента STA является то, что разработка объекта COM и клиентской части проста в том смысле, что нет нужды заботиться о синхронизации вызовов методов и их выполнения: следующий метод начнет выполняться только после того, как закончится выполнение предыдущего. Аналогично не нужно заботиться о синхронизации доступа к данным объекта.
Главным недостатком апартамента STA является его неэффективность. Даже если синхронизация вызовов методов и не нужна, она все равно выполняется.
Несмотря на это, предпочтительнее всегда использовать апартамент STA за исключением тех случаев, когда реализация конкретного сервера в рамках модели STA попросту невозможна.
Многопоточные апартаменты.
В MTA несколько потоков в одном апартаменте и обращения потоков объект должен синхронизировать самостоятельно.
MTA апартаменты имеют произвольное число потоков, так что все объекты, которые «живут» в таком апартаменте, могут принимать вызовы их методов от любого из потоков, который принадлежит этому апартаменту. Потоки в MTA использует модель, называемую свободной потоковой (free-threading). Для MTA COM автоматически поддерживает пул потоков и при поступлении очередного вызова метода отыскивает любой свободный поток и передает обработку ему. Таким образом, в любой момент времени клиент может вызывать разные методы из разных потоков или один и тот же метод. Если какой-либо метод объекта выполняется длительное время, это не означает, что другой клиент или поток будет ожидать завершения этого метода.
Основным достоинством MTA апартамента является потенциально высокая производительность сервера, вследствие чего он более доступен для клиентов. Вместе с тем разработка объектов такого сервера намного сложнее, так как требуется синхронизировать не только вызовы методов, но и обращение к локальным данным объектов.
Любой процесс может иметь произвольное число STA и MTA апартаментов, в том числе нулевое. В зависимости от их числа модели потоков сервера (threading model) классифицируются следующим образом:
Single. Если процесс имеет только один STA апартамент, то эта модель называется моделью одного потока (single-threaded model ). Эта модель используется обычно для внутрипроцессных серверов (DLL библиотека) и при их регистрации в реестре не создается ключ Threadingmodel (Single в Delphi). Нет поддержки потоков – запросы клиентов выполняются последовательно. Apartment. Процесс, имеющий несколько STA апартаментов и ни одного апартамента MTA, использует модель разделенных потоков (apartment-threaded model). Для DLL-сервера в реестре будет записан ключ Threadingmodel=”Apartment”, а для EXE-сервера будет создан апартамент STA. Клиенты могут вызывать методы объекта только из потока, в котором объект был создан. Различные объекты одного и того же сервера могут быть вызваны из разных потоков, однако каждый объект может вызываться только из одного потока. Локальные данные каждого экземпляра объекта в безопасности, а глобальные данные должны быть защищены с использованием критических секций или каким-либо иным способом. Имеет некоторые преимущества. Объекты легки в разработке, однако клиентам придется решать более сложные задачи. Используется в первую очередь в управляющих элементах Web браузеров Free. Процесс имеет один или несколько MTA апартаментов и ни одного STA. Эта модель называется моделью свободных потоков (free-threaded model). Для DLL-сервера в реестре будет записан ключ Threadingmodel=”Free”, а для EXE-сервера будет создан апартамент MTA. Клиенты могут вызывать любые методы объекта из любого потока в любое время. Объекты могут обрабатывать любое число потоков в любое время. Они должны защищать глобальные данные и данные экземпляров с помощью критических секций или каким-либо другим методом, защищающим некоторую последовательность кода. Клиенты программируются легко, однако объекты более сложны в разработке. Используется в первую очередь в распределенных средах DCOM Both. Модель смешанных потоков (both-threded model): имеются MTA и STA апартаменты. Для DLL-сервера в реестре будет записан ключ Threadingmodel=”Both”, а для EXE-сервера будет создан апартамент MTA. Объекты могут поддерживать клиентов, которые используют модели apartment или free. Максимум производительности и гибкости. Клиенты могут использовать одно-поточную или многопоточную модель для повышения производительности Neutral. Модель нейтральных потоков (neutral-threaded model), близкая к модели Both. Для DLL-сервера в реестре будет записан ключ Threadingmodel=”Neutral”, а для EXE-сервера будет создан апартамент MTAМодель нейтральных потоков отличается от модели смешанных потоков тем, что, во-первых, для ее поддержки нужна COM+, и, во-вторых, она несколько упрощает синхронизацию потоков. Суть синхронизации заключается в том, что если какой-либо клиент вызвал метод объекта, то другой клиент не сможет вызвать этот же метод до тех пор, пока метод не завершится. Если все методы работают только со «своими» данными, то в этом случае отпадает необходимость и в синхронизации доступа к полям объекта.
Отметим, что указание какой-либо потоковой модели в мастере Delphi является не более чем декларацией (для COM): соответствующую реализацию программист должен обеспечить сам.
Выбор способа создания экземпляра объекта (instancing mode). Если COM объект используется только внутри процесса, значение этого поля игнорируется. Значения этого поля интерпретируются так:
Internal |
Объект может быть создан только в клиентском приложении, а внешнее приложение не может создать экземпляр объекта непосредственно. Например, текстовый редактор может иметь объект документ, который может быть создан только с помощью метода редактора |
Single Instance |
Если некоторое приложение связалось с объектом, то уже никакое другое приложение не может получить доступ к объекту. Этот режим обычно используется для MDI приложений. Когда клиент запрашивает сервис у такого объекта, все запросы обрабатываются одним и тем же сервером. Например, всякий раз, когда клиент открывает новый документ в текстовом редакторе, этот новый документ открывается в том же процессе |
Multiple Instance |
Указывает, что несколько приложений могут связываться с объектом. Всякий раз при выполнении запроса новым клиентом, создается новый экземпляр сервера. Например, когда пользователь запускает Explorer Windows, создается новый экземпляр Explorer |
Комментарии к потоковым моделям.
Обе стороны приложения – клиентская и серверная – указывают COM, каким она должна следовать правилам в использовании потоков при инициализации COM объекта. Если обе стороны поддерживают одну и ту же модель, COM устанавливает между ними прямую связь и считает, что обе стороны соблюдают правила взаимодействия. Если стороны придерживаются разных правил, COM включает механизм маршалинга так, что каждая из сторон видит только те правила, которые она знает. Конечно, это снижает производительность, однако обеспечивает корректное взаимодействие клиента и сервера.
Необходимо отметить, что выбранная в мастере потоковая модель только то, как объект заявляет себя в реестре. Разработчик должен сам обеспечить соответствующую выбранной модели реализацию объекта.
Потоковая модель подлинна только для внутрипроцессных серверов. Установка потоковой модели в мастере обеспечивает только установку соответствующей модели в системном реестре для внутрипроцессного сервера. Внепроцессные (локальные и удаленные) серверы регистрируются как исполняемые файлы (exe) и разработчик сам должен побеспокоиться о соответствующей реализации модели.
Замечание. Локальные переменные всегда в безопасности независимо от потоковой модели, так как они размещаются в стеке, а каждый поток имеет свой собственный стек.
«55. Интерфейсы»
57. Обработка ошибок в Windows