Интернет-журнал "Домашняя лаборатория", 2007 №6 - Усманов
Шрифт:
Интервал:
Закладка:
Теперь клиент и сервер не только запускаются в различных процессах, но и могут обмениваться друг с другом необходимыми данными.
Говоря про маршалинг следует остановиться на вопросе передачи данных между клиентом и сервером. В случае, когда передаются, например, массивы, необходимо следить за тем, кто выделяет и освобождает память, сколько выделяется памяти. Приведенные ниже примеры взяты из работы Richard Grimes, "Marshaling Your Data: Efficient Data Transfer Techniques Using COM and Windows 2000", MSDN Magazine, September 2000.
Предположим, что некоторый интерфейс включает методы, описания которых на IDL приведено ниже
HRESULT PassLongs([in] ULONG ulNum,
[in, size_is(ulNum)] LONG* pArrln);
HRESULT GetLongs([in] ULONG ulNum,
[out, size_is(ulNum)] LONG* pArrOut);
Здесь от клиента к объекту и обратно передаются массивы. Проще всего случай передачи массива от клиента к объекту. В этом случае клиент сам выделяет память под массив и сам ее освобождает. При передаче массива от объекта к клиенту возможны два варианта. В первом клиент заранее, до вызова метода знает размер передаваемого массива, во втором этот размер сообщает объект уже после вызова метода. Рассмотрим обе эти возможности.
Начнем со случая, когда клиент до вызова метода имеет информацию о размере массива. Код клиента
ULONG ulNum = 10;
LONG 1 [10];
hr = pArrObj — > GetLongs(ulNum, 1);
Код сервера
STDMETHODIMP CArrays::GetLongs(ULONG ulNum, LONG* pArr)
{
for (ULONG x = 0; x < ulNum; x++) pArr[x] = x;
return S_OK;
}
Как все это работает. Клиент сам выделяет память под массив в виде стековой переменной. Это позволяет ему не заботится об ее освобождении. Маршалер (система объектов, обеспечивающая маршализацию данных, передаваемых между клиентом и сервером) как на стороне клиента, так и на стороне объекта имеет информацию о размере передаваемого массива еще до возврата из метода. Это позволяет ему выделить на стороне объекта память под этот массив и передать указатель на выделенную память объекту (рАгг). Объект заполняет массив данными, после чего эти данные передаются по каналу клиенту. После передачи данных маршалер сам освобождает выделенную на стороне объекта память.
Во втором случае клиент получает размер массива уже после вызова метода. На IDL описание метода выглядит следующим образом
HRESULT GetLongsAlloc([out] ULONG* pNum, [out, size_is(, *pNum)] LONG** ppArr);
Здесь запятая в size is () говорит о том, что *pNum есть размер массива, УКАЗАТЕЛЬ на который есть ppArr.
В данном случае маршалер не может сам выделить память под массив на стороне объекта и, следовательно, это должен делать объект. Но освобождать выделенную память после передачи данных должен уже маршалер, т. к. объект к этому моменту уже завершит работу. Для выделения и освобождения памяти в данном случае следует использовать функции CoTaskMemAlloc () и CoTaskMemFree (), которые может вызывать и маршалер.
Код компонента
STDMETHODIMP CArrays::GetLongsAlloc(ULONG* pNum, LONG** ppArr);
{
*pNum = 10;
*ppArr = reinterpret_cast<LONG*>(CoTaskMemAlloc(*pNum * sizeof(LONG)));
for (ULONG x = 0; x < *pNum; x++) (*ppArr)[x] = x;
return S_OK;
}
Здесь reinterpret_cast<LONG*> используется для приведения типа указателя, возвращаемого при выделении памяти, к указателю на long. Освобождает выделенную память сам маршалер, вызывая CoTaskMemFree.
На стороне клиента память выделяет маршалер, получив информацию о размере массива — *pNum. Освобождает же эту память клиент
ULONG ulNum;
LONG* pi;
hr = pArrObj —> GetLongsAlloc(SulNum, &pl);
for (ULONG ul = 0; ul < ulNum; ul++)
printf("%ldn", pi[ul]);
CoTaskMemFree(pi);
Потоковая модель и апартаменты
Процесс и поток
Прежде всего вспомним некоторые понятия, связанные с процессами и потоками.
Приложение — это один или несколько процессов.
Каждый процесс имеет ряд ресурсов, среди которых виртуальное адресное пространство процесса, исполняемый код, данные, дескрипторы объектов, переменные среды, базовый приоритет. Таким образом, процесс это скорее среда, в которой выполняется один или несколько потоков.
Поток — выполнение последовательности машинных команд. Потоки, живущие в одном процессе, делят все его ресурсы. Кроме того, каждый поток имеет и свои собственные, доступные только ему ресурсы-локальная память потока.
Операционная система делит процессорное время между потоками. Очередность получения выделенного кванта времени определяется приоритетом потока. По завершении выделенного кванта времени система сохраняет контекст исполняемого потока (некоторый набор данных, хранящий состояние прерванного потока) и восстанавливает контекст очередного потока, получающего новый квант времени.
Очередь потоков регулируется с помощью их приоритетов. Для каждого уровня приоритета имеется своя очередь. Переключение контекста (прерывание исполняемого потока и выполнение нового) происходит при возникновении одного из трех событий:
• закончился квант времени, выделенный потоку;
• готов к выполнению поток с более высоким приоритетом;
• исполняемый поток перешел в состояние ожидания.
Базовый приоритет потока определяется двумя величинами: приоритетом процесса и приоритетом потока в рамках данного процесса. Очередность определяется не базовым, а динамическим приоритетом. Часто динамический приоритет просто совпадает с базовым, но в некоторых случаях система может приписать потоку динамический приоритет, превосходящий его базовый приоритет.
Например, если поток выполняет операции ввода/вывода, то система может приписать ему повышенный динамический приоритет. Однако, этот приоритет уменьшается на 1 после получения данным потоком очередного кванта процессорного времени и может достигнуть базового значения. Ниже базового значения приоритет не уменьшается.
Еще один пример повышенного динамического приоритета связан с тем случаем, когда некоторый поток с высоким приоритетом ожидает завершения потока с низким приоритетом. Обычно в таком случае поток с низким приоритетом просто прерывается, но возможны ситуации, когда выполнение потока с высоким приоритетом остается заблокированным. Например, если поток с низким приоритетом вошел в критическую секцию (далее будет объяснено это понятие), и в эту же критическую секцию хочет войти поток с высоким приоритетом. Пока поток, находящийся в критической секции, не выйдет из нее, никто не сможет туда войти. В этом случае система приписывает потоку в критической секции высокий приоритет с тем, чтобы этот поток быстрее завершил свою работу.
Как порождаются потоки? Первый поток образуется при выполнении функции main. Новый поток можно запустить