Синхронизация потоков
Описание способов повышения производительности и реализации программ при помощи потоков. Изучение схемы разделения общей памяти несинхронизированными потоками, критические участи программного кода. Функции синхронизации и взаимоблокировки потоков.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | дипломная работа |
Язык | русский |
Дата добавления | 10.04.2013 |
Размер файла | 1,0 M |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Следует помнить, что функция PeekNamedPipe осуществляет чтение, не уничтожая данные, и поэтому для удаления сообщений или байтовых данных из канала требуется последующее применение функции ReadFile.
Каналы UNIX FIFO аналогичны именованным каналам и, таким образом, обеспечивают взаимодействие не связанных между собой процессов. Однако по сравнению с именованными каналами Windows их возможности являются несколько ограниченными:
- Каналы FIFO являются полудуплексными.
- Каналы FIFO действуют только в пределах одного компьютера.
- Каналы FIFO ориентированы на работу с байтами, поэтому в клиент-серверных приложениях проще всего использовать записи фиксированной длины. Тем не менее, отдельные операции чтения и записи являются атомарными.
Сервер, на котором применяется это средство, должен использовать для каждого ответа клиентам отдельный канал FIFO, хотя все клиенты могут посылать запросы по одному и тому же известному каналу. В соответствии с общепринятой практикой клиенты включают имя канала FIFO в запрос соединения.
Функция UNIX mkfifo является ограниченной версией функции CreateNamedFile.
Если клиенты и сервер должны находиться в сети, используйте сокеты или аналогичный механизм транспортировки сетевых сообщений. Сокеты работают в дуплексном режиме, однако требуют использования отдельного соединения для каждого клиента.
Пример: клиент-серверный процессор командной строки
Теперь мы располагаем всем необходимым для построения клиент-серверной системы, работающей с запросами и ответами. В данном примере будет представлен сервер командной строки, выполняющий команду по требованию клиента. Система характеризуется следующими особенностями:
- С сервером могут взаимодействовать несколько клиентов.
- Клиенты могут находиться на различных системах в сети, хотя допускается и их расположение на компьютере сервера.
- Сервер является многопоточным, причем каждому именованному каналу назначается отдельный поток. Это означает, что существует пул потоков (thread pool), в который входят рабочие потоки, готовые к использованию подключающимися клиентами. Рабочие потоки предоставляются клиентам посредством экземпляра именованного канала, который система выделяет клиенту.
- Отдельные потоки сервера в каждый момент времени обрабатывают один запрос, что упрощает управление параллелизмом их выполнения. Каждый из потоков самостоятельно обрабатывает свои запросы. Тем не менее, требуется предпринимать обычные меры предосторожности на тот случай, если несколько различных потоков сервера пытаются получить доступ к одному и тому же файлу или иному ресурсу.
В программе 11.2 представлен однопоточной клиент, а в программе 11.3 -- его сервер. Сервер соответствует модели, представленной на рисунках 7.1 и 11.2. Запросом клиента является обычная командная строка. Ответом сервера является результирующий вывод, который посылается в виде нескольких сообщений. Кроме того, в программе используется находящийся на Web-сайте заголовочный файл ClntSrvr.h, в котором определены структуры данных запроса и ответа, а также имена каналов клиента и сервера.
В программе 11.2 клиент вызывает функцию LocateServer, которая находит имя канала сервера. Функция LocateServer использует почтовый ящик (mailslot), описанный в одном из последующих разделов и представленный в программе 11.5.
В объявлениях записей имеются поля длины, тип которых определен как DWORD32; это сделано для того, чтобы программы, получая возможность их последующего перенесения на платформу Win64, могли взаимодействовать с серверами и клиентами, выполняющимися под управлением любой системы Windows.
Программа 11.2. clientNP: клиент, ориентированный на соединение посредством именованного канала
/* Глава 11. Клиент-серверная система. ВЕРСИЯ КЛИЕНТА.
clientNP -- клиент, ориентированный на установку соединения. */
/* Выполнить командную строку (на сервере); отобразить ответ. */
/* Клиент создает долговременное соединение с сервером (захватывая */
/* экземпляр канала) и выводит приглашение пользователю для ввода команд.*/
#include "EvryThng.h"
#include "ClntSrvr.h" /* Определяет структуры записей запроса и ответа. */
int _tmain(int argc, LPTSTR argv[]) {
HANDLE hNamedPipe = INVALID_HANDLE_VALUE;
TCHAR PromptMsg[] = _T("\nВведите команду: ");
TCHAR QuitMsg[] = _T("$Quit");
TCHAR ServerPipeName[MAX_PATH];
REQUEST Request; /* См. файл ClntSrvr.h. */
RESPONSE Response; /* См. файл ClntSrvr.h. */
DWORD nRead, nWrite, NpMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
LocateServer(ServerPipeName);
/* Ожидать появления экземпляра именованного канала и "вступить в борьбу" за право его открытия. */
while (INVALID_HANDLE_VALUE == hNamedPipe) {
WaitNamedPipe(ServerPipeName, NMPWAIT_WAIT_FOREVER);
hNamedPipe = CreateFile(ServerPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
/* Задать блокирование дескриптора именованного канала; режим сообщений.*/
SetNamedPipeHandleState(hNamedPipe, &NpMode, NULL, NULL);
/* Вывести приглашение пользователю для ввода команд. Завершить выполнение по получении команды "$quit." */
while (ConsolePrompt(PromptMsg, Request.Record, MAX_RQRS_LEN, TRUE) && (_tcscmp(Request.Record, QuitMsg) != 0)) {
WriteFile(hNamedPipe, &Request, RQ_SIZE, &nWrite, NULL);
/* Считать каждый ответ и направить его на стандартный вывод.
Response.Status = 0 означает "конец ответного сообщения." */
while (ReadFile(hNamedPipe, &Response, RS_SIZE, &nRead, NULL) && (Response.Status == 0)) _tprintf(_T("%s"), Response.Record);
}
_tprintf(_T("Получена команда завершения работы. Соединение разрывается."));
CloseHandle(hNamedPipe);
return 0;
}
Программа 11.3 -- это серверная программа, включающая функцию потока сервера, которая обрабатывает запросы, генерируемые с помощью программы 11.2. Кроме того, сервер создает "широковещательный серверный поток" ("server broadcast thread") (см. программу 11.4), который используется для широковещательной рассылки имени своего канала всем клиентам, желающим подключиться, посредством почтового ящика. В программе 11.2 вызывается функция LocateServer, представленная в программе 11.5, которая считывает информацию, отправленную данным процессом. Почтовые ящики описываются далее в настоящей главе. Хотя соответствующий код и не включен в программу 11.4, в ней предусмотрена возможность защиты сервером (представлен на Web-сайте) своего именованного канала с целью предотвращения доступа к нему клиентов, не имеющих должных полномочий. Вопросы безопасности объектов рассматриваются в главе 15, где будет также показано, как использовать указанную возможность.
Программа 11.3. serverNP: многопоточный сервер именованного канала
/* Глава 11. ServerNP. */
/* Многопоточный сервер командной строки. Версия на основе именованных каналов. */
#include "EvryThng.h"
#include "ClntSrvr.h" /* Определения сообщений запроса и ответа. */
typedef struct { /* Аргумент серверного потока. */
HANDLE hNamedPipe; /* Экземпляр именованного канала. */
DWORD ThreadNo;
TCHAR TmpFileName[MAX_PATH]; /* Имя временного файла. */
} THREAD_ARG;
typedef THREAD_ARG *LPTHREAD_ARG;
volatile static BOOL ShutDown = FALSE;
static DWORD WINAPI Server(LPTHREAD_ARG);
static DWORD WINAPI Connect(LPTHREAD_ARG);
static DWORD WINAPI ServerBroadcast(LPLONG);
static BOOL WINAPI Handler(DWORD);
static TCHAR ShutRqst[] = _T("$ShutDownServer");
_tmain(int argc, LPTSTR argv[]) {
/* Определение MAX_CLIENTS содержится в файле ClntSrvr.h. */
HANDLE hNp, hMonitor, hSrvrThread[MAXCLIENTS];
DWORD iNp, MonitorId, ThreadId;
LPSECURITY_ATTRIBUTES pNPSA = NULL;
THREAD_ARG ThArgs[MAXCLIENTS];
/* Обработчик управляющих сигналов консоли, используемый для остановки сервера. */
SetConsoleCtrlHandler(Handler, TRUE);
/* Периодически создавать имя широковещательного канала потока. */
hMonitor = (HANDLE)_beginthreadex(NULL, 0, ServerBroadcast, NULL, 0, &MonitorId);
/* Создать экземпляр канала и временный файл для каждого серверного потока. */
for (iNp = 0; iNp < MAX_CLIENTS; iNp++) {
hNp = CreateNamedPipe(SERVER_PIPE, PIPE_ACCESS_DUPLEX, PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE | PIPE_WAIT, MAXCLIENTS, 0, 0, INFINITE, pNPSA);
ThArgs[iNp].hNamedPipe = hNp;
ThArgs[iNp].ThreadNo = iNp;
GetTempFileName(_T("."), _T("CLP"), 0, ThArgs[iNp].TmpFileName);
hSrvrThread[iNp] = (HANDLE)_beginthreadex(NULL, 0, Server, &ThArgs[iNp], 0, &ThreadId);
}
/* Ждать завершения выполнения всех потоков. */
WaitForMultipleObjects(MAXCLIENTS, hSrvrThread, TRUE, INFINITE);
WaitForSingleObject(hMonitor, INFINITE);
CloseHandle(hMonitor);
for (iNp = 0; iNp < MAXCLIENTS; iNp++) {
/* Закрыть дескрипторы канала и удалить временные файлы. */
CloseHandle(hSrvrThread[iNp]);
DeleteFile(ThArgs[iNp].TmpFileName);
}
_tprintf(_T("Серверный процесс завершил выполнение.\n"));
return 0;
}
static DWORD WINAPI Server(LPTHREAD_ARG pThArg)
/* Функция потока сервера; по одной для каждого потенциального клиента. */
{
HANDLE hNamedPipe, hTmpFile = INVALID_HANDLE_VALUE, hConTh, hClient;
DWORD nXfer, ConThId, ConThStatus;
STARTUPINFO StartInfoCh;
SECURITY_ATTRIBUTES TempSA = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
PROCESS_INFORMATION ProcInfo;
FILE *fp;
REQUEST Request;
RESPONSE Response;
GetStartupInfo(&StartInfoCh);
hNamedPipe = pThArg->hNamedPipe;
hTmpFile = CreateFile(pThArg->TmpFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &TempSA, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
while (!ShutDown) { /* Цикл соединений. */
/* Создать поток соединения; ждать его завершения. */
hConTh = (HANDLE)_beginthreadex(NULL, 0, Connect, pThArg, 0, &ConThId);
/* Ожидание соединения с клиентом и проверка флага завершения работы.*/
while (!ShutDown && WaitForSingleObject(hConTh, CS_TIMEOUT) == WAIT_TIMEOUT) { /* Пустое тело цикла. */ };
CloseHandle(hConTh);
if (ShutDown) continue; /*Флаг может быть установлен любым потоком.*/
/* Соединение существует. */
while (!ShutDown && ReadFile(hNamedPipe, &Request, RQ_SIZE, &nXfer, NULL)) {
/* Получать новые команды до отсоединения клиента. */
ShutDown = ShutDown || (_tcscmp(Request.Record, ShutRqst) == 0);
if (ShutDown) continue; /* Проверяется на каждой итерации. */
/* Создать процесс для выполнения команды. */
StartInfoCh.hStdOutput = hTmpFile;
StartInfoCh.hStdError = hTmpFile;
StartInfoCh.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
StartInfoCh.dwFlags = STARTF_USESTDHANDLES;
CreateProcess(NULL, Request.Record, NULL, NULL, TRUE, /* Унаследовать дескрипторы. */
0, NULL, NULL, &StartInfoCh, &ProcInfo);
/* Выполняется процесс сервера. */
CloseHandle(ProcInfo.hThread);
WaitForSingleObject(ProcInfo.hProcess, INFINITE);
CloseHandle(ProcInfo.hProcess);
/* Отвечать по одной строке за один раз. Здесь удобно использовать функции библиотеки С для работы со строками. */
fp = _tfopen(pThArg->TmpFileName, _T("r"));
Response.Status = 0;
while(_fgetts(Response.Record, MAX_RQRS_LEN, fp) != NULL) WriteFile(hNamedPipe, &Response, RS_SIZE, &nXfer, NULL);
FlushFileBuffers(hNamedPipe);
fclose(fp);
/* Уничтожить содержимое временного файла. */
SetFilePointer(hTmpFile, 0, NULL, FILE_BEGIN);
SetEndOfFile(hTmpFile);
/* Отправить признак конца ответа. */
Response.Status = 1;
strcpy(Response.Record, "");
WriteFile(hNamedPipe, &Response, RS_SIZE, &nXfer, NULL);
}
/* Конец основного командного цикла. Получить следующую команду. */
/* Принудительно завершить выполнение потока, если он все еще активен.*/
GetExitCodeThread(hConTh, &ConThStatus);
if (ConThStatus == STILL_ACTIVE) {
hClient = CreateFile(SERVER_PIPE, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN EXISTING, FILE ATTRIBUTE NORMAL, NULL);
if (hClient != INVALID_HANDLE_VALUE) CloseHandle (hClient);
WaitForSingleObject (hConTh, INFINITE);
}
/* Клиент отсоединился или имеется запрос останова. */
FlushFileBuffers(hNamedPipe);
DisconnectNamedPipe(hNamedPipe);
}
/* Конец командного цикла. Освободить ресурсы; выйти из потока. */
if (hTmpFile != INVALID_HANDLE_VALUE) CloseHandle(hTmpFile);
DeleteFile(pThArg->TmpFileName);
_tprintf(_T("Выход из потока номер %d\n"), pThArg->ThreadNo);
_endthreadex(0);
}
static DWORD WINAPI Connect(LPTHREAD_ARG pThArg) {
/* Поток соединения разрешает серверу опрос флага ShutDown. */
ConnectNamedPipe(pThArg->hNamedPipe, NULL);
_endthreadex(0);
return 0;
}
BOOL WINAPI Handler(DWORD CtrlEvent) {
/* Завершить работу системы. */
ShutDown = TRUE;
return TRUE;
}
Комментарии по поводу клиент-серверного процессора командной строки
Данное решение характеризуется рядом особенностей и ограничений, которые будут обсуждаться в последующих главах.
- Соединяться с сервером и выполнять параллельные запросы могут сразу несколько серверов; каждому клиенту назначается серверный (или рабочий) поток, выделяемый из пула потоков.
- Сервер и клиенты могут выполняться либо в ответ на отдельные подсказки командной строки, либо под управлением программы JobShell (программа 6.3).
- Если во время попыток клиента соединиться с сервером все экземпляры именованного канала оказываются задействованными, то новый клиент будет находиться в состоянии ожидания до тех пор, пока другой клиент не разорвет соединение в ответ на получение команды $Quit, тем самым делая его доступным для ожидающего клиента. Возможны ситуации, когда сразу несколько новых клиентов будут одновременно пытаться создать соединение с сервером, соревнуясь между собой за право открытия доступного экземпляра; потоки, проигравшие в этой конкурентной борьбе, будут вынуждены вновь перейти в состояние ожидания.
- Каждый серверный поток выполняет синхронные операции ввода/вывода, но одни из этих потоков могут обрабатывать запросы, в то время как другие -- ожидать соединения или поступления клиентских запросов.
- С учетом ограничений, свойственных именованным каналам, о чем говорилось ранее в этой главе, расширение программы на случай сетевых клиентов не составляет труда. Для этого достаточно заменить имена каналов в заголовочном файле или добавить параметр, указывающий имя сервера в командной строке клиента.
- Каждый рабочий поток сервера создает простой поток, осуществляющий соединение, который вызывает функцию ConnectNamedPipe и завершает выполнение сразу же после подключения клиента. Это позволяет организовать ожидание дескриптора потока соединения рабочим потоком с использованием конечного интервала ожидания и периодическое тестирование глобального флага завершения работы (ShutDown). Если бы рабочие потоки блокировались при выполнении функции ConnectNamedPipe, они не могли бы тестировать этот флаг, и сервер не мог бы завершить работу. По этой причине поток сервера осуществляет вызов CreateFile, используя дескриптор именованного канала, чтобы заставить поток соединения возобновиться и завершить выполнение. Альтернативным вариантом было бы использование асинхронного ввода/вывода (глава 14), что дало бы возможность связать событие с вызовом функции ConnectNamedPipe. Другие возможные варианты реализации и дополнительная информация предоставляются в комментариях к исходному тексту программы, размещенному на Web-сайте книги. Без этого решения потоки соединения могли бы никогда не завершить работу самостоятельно, что привело бы к утечке ресурсов в DLL. Этот вопрос обсуждается в главе 12.
- Существует ряд благоприятных предпосылок для усовершенствования данной системы. Например, можно предусмотреть опцию выполнения внутрипроцессного сервера (in-process server), используя библиотеку DLL, которая реализует некоторые из команд. Это усовершенствование вводится в программу в главе 12.
- Количество серверных потоков ограничивается при вызове функции WaitForMultipleObjects в основном потоке. Хотя это ограничение легко преодолимо, в данном случае система не обладает истинной масштабируемостью; как было показано в главе 10, чрезмерное увеличение количества потоков может оказать отрицательное влияние на производительность. В главе 14 для решения этой проблемы используются порты асинхронного ввода/вывода.
Почтовые ящики
Как и именованные каналы, почтовые ящики (mailslots) Windows снабжаются именами, которые могут быть использованы для обеспечения взаимодействия между независимыми каналами. Почтовые ящики представляют собой широковещательный механизм, основанный на дейтаграммах (описаны в главе 12), и ведут себя иначе по сравнению с именованными каналами, что делает их весьма полезными в ряде ограниченных ситуаций, которые, тем не менее, представляют большой интерес. Из наиболее важных свойств почтовых ящиков можно отметить следующие:
- Почтовые ящики являются однонаправленными.
- С одним почтовым ящиком могут быть связаны несколько записывающих программ (writers) и несколько считывающих программ (readers), но они часто связаны между собой отношениями "один ко многим" в той или иной форме.
- Записывающей программе (клиенту) не известно достоверно, все ли, только некоторые или какая-то одна из программ считывания (сервер) получили сообщение.
- Почтовые ящики могут находиться в любом месте сети.
- Размер сообщений ограничен.
Использование почтовых ящиков требует выполнения следующих операций:
- Каждый сервер создает дескриптор почтового ящика с помощью функции CreateMailSlot.
- После этого сервер ожидает получения почтового сообщения, используя функцию ReadFile.
- Клиент, обладающий только правами записи, должен открыть почтовый ящик, вызвав функцию CreateFile, и записать сообщения, используя функцию WriteFile. В случае отсутствия ожидающих программ считывания попытка открытия почтового ящика завершится ошибкой (наподобие "имя не найдено").
Сообщение клиента может быть прочитано всеми серверами; все серверы получают одно и то же сообщение.
Существует еще одна возможность. В вызове функции CreateFile клиент может указать имя почтового ящика в следующем виде:
\\*\mailslot\mailslotname
При этом символ звездочки (*) действует в качестве группового символа (wildcard), и клиент может обнаружить любой сервер в пределах имени домена -- группы систем, объединенных общим именем, которое назначается администратором сети.
Использование почтовых ящиков
Рассмотренный перед этим клиент-серверный процессор командной строки предполагает несколько возможных способов его использования. Рассмотрим один из сценариев, в котором решается задача обнаружения сервера в только что упомянутой клиент-серверной системе (программы 11.2 и 11.3).
Сервер приложения (application server), действуя в качестве почтового клиента (mailslot client), периодически осуществляет широковещательную рассылку своего имени и имени именованного канала. Любой клиент приложения (application client), которому требуется найти сервер, может получить это имя, действуя в качестве сервера почтовых ящиков (mailslot server). Аналогичным образом сервер командной строки может периодически осуществлять широковещательную рассылку своего состояния, включая информацию о коэффициенте использования, клиентам. Это соответствует ситуации, в которой имеется одна записывающая программа (почтовый клиент) и несколько считывающих программ (почтовых серверов). Если бы почтовых клиентов (то есть серверов приложения) было несколько, то ситуация описывалась бы отношением типа "многие ко многим".
Возможен и другой вариант, когда одна считывающая программа получает сообщения от многочисленных записывающих программ, которые, например, предоставляют информацию о своем состоянии. Этот вариант, соответствующий, например, электронной доске объявлений, оправдывает использование термина почтовый ящик. Оба описанных варианта использования -- широковещательная рассылка имени и информации о состоянии -- могут быть объединены, чтобы клиент мог выбирать наиболее подходящий сервер.
Обмен ролями терминов клиент и сервер в данном контексте может несколько сбивать с толку, однако заметьте, что сервер именованного канала и почтовый сервер выполняют вызовы функций CreateNamedPipe (или CreateMailSlot), тогда как клиент (именованного канала или почтового ящика) создает соединение, используя функцию CreateFile. Кроме того, в обоих случаях первый вызов функции WriteFile выполняется клиентом, а первый вызов функции ReadFile выполняется сервером.
Использование почтовых ящиков в соответствии с первым из описанных возможных вариантов иллюстрируется на рис. 11.3.
Создание и открытие почтового ящика
Для создания почтового ящика и получения дескриптора, который можно будет использовать в операциях ReadFile, почтовые серверы (программы считывания) вызывают функцию CreateMailslot. На одном компьютере может находиться только один почтовый ящик с данным именем, но один и тот же почтовый ящик может использоваться несколькими системами в сети, что обеспечивает возможность работы с ним нескольких программ считывания.
Рис. 11.3. Использование клиентами почтового ящика для обнаружения сервера
HANDLE CreateMailslot(LPCTSTR lpName, DWORD cbMaxMsg, DWORD dwReadTimeout, LPSECURITY_ATTRIBUTES lpsa)
Параметры
lpName -- указатель на строку с именем почтового ящика, которое должно иметь следующий вид:
\\.\mailslot\[путь]имя
Имя должно быть уникальным. Точка (.) указывает на то, что почтовый ящик создается на локальном компьютере.
cbMaxMsg -- максимальный размер сообщения (в байтах), которые может записывать клиент. Значению 0 соответствует отсутствие ограничений.
dwReadTimeOut -- длительность интервала ожидания (в миллисекундах) для операции чтения. Значению 0 соответствует немедленный возврат, а значению MAILSLOT_WAIT_FOREVER -- неопределенный период ожидания (который может длиться сколь угодно долго).
Во время открытия почтового ящика с помощью функции CreateFile клиент (записывающая программа) может указывать его имя в следующем виде:
- \\ .\mailslot\ [путь]имя -- определяет локальный почтовый ящик. Примечание. В Windows 95 длина имени ограничена 11 символами.
- \\имя_компьютера\mailslot\[путь]имя -- определяет почтовый ящик, расположенный на компьютере с заданным именем.
- \\имя_домена\mailslot\[путь]имя -- определяет все почтовые ящики с данным именем, расположенные на компьютерах, принадлежащих данному домену. В этом случае максимальный размер сообщения составляет 424 байта.
- \\*\mailslot\[путь]имя -- определяет все почтовые ящики с данным именем, расположенные на компьютерах, принадлежащих главному домену системы. В этом случае максимальный размер сообщения составляет 424 байта.
Наконец, клиент должен указывать флаг FILE_SHARE_READ. Функции GetMailslotInfo и SetMailslotInfо похожи на свои аналоги, работающие с именованными каналами.
Средства, сопоставимые с почтовыми ящиками, в UNIX отсутствуют. Однако для этой цели могут быть использованы широковещательные (broadcast) или групповые (multicast) дейтаграммы протокола TCP/IP.
Создание, подключение и именование каналов и почтовых ящиков
В табл. 11.1 сведены все допустимые формы имен каналов, которые могут использоваться клиентами и серверами приложения. Здесь же перечислены все функции, которые следует использовать для создания именованных каналов и соединения с ними.
Аналогичная информация для почтовых ящиков приведена в табл. 11.2. Вспомните, что почтовый клиент (или сервер) не обязательно должен выполняться тем же процессом или даже на той же системе, что и клиент (или сервер) приложения.
Таблица 11.1. Именованные каналы: создание, подключение и именование
Клиент приложения |
Сервер приложения |
||
Дескриптор именованного канала или соединение |
CreateFile CallNamedPipe TransactNamedPipe |
CreateNamedPipe |
|
Имя канала |
\\.\имя канала (канал является локальным) \\имя системы\имя канала (канал является локальным или удаленным) |
\\.\имя канала (канал создается локальным) |
Таблица 11.2. Почтовые ящики: создание, подключение и именование
Почтовый клиент |
Почтовый сервер |
||
Дескриптор почтового ящика |
CreateFile |
CreateMailslot |
|
Имя почтового ящика |
\\.\имя почтового ящика (почтовый ящик является локальным) \\имя системы\имя почтового ящика (почтовый ящик располагается на указанной удаленной системе) \\*\имя почтового ящика (все почтовые ящики, имеющие одно и то же указанное имя) |
\\.\имя почтового ящика (почтовый ящик создается локальным) |
Пример: сервер, обнаруживаемый клиентами
Программа 11.4 представляет функцию потока, которую сервер командной строки (программа 11.3), выступающий в роли почтового клиента, использует для широковещательной рассылки имени своего канала ожидающим клиентам. Возможно существование нескольких серверов с различными характеристиками и именами каналов, и клиенты получают их имена, используя почтовый ящик с известным именем. Эта функция запускается как поток программой 11.3.
Примечание
На практике многие клиент-серверные системы инвертируют используемую здесь логику поиска. Суть альтернативного варианта заключается в том, что клиент приложения действует и как почтовый клиент, осуществляя широковещательную рассылку сообщений, требующих, чтобы сервер ответил с использованием указанного именованного канала (имя канала определяется клиентом и включается в сообщение). Затем сервер приложения, действующий в качестве почтового сервера, считывает запрос и создает соединение с использованием указанного именованного канала.
Программа 11.4. SrvrBcst: функция потока почтового клиента
static DWORD WINAPI ServerBroadcast(LPLONG pNull) {
MS_MESSAGE MsNotify;
DWORD nXfer;
HANDLE hMsFile;
/*Открыть почтовый ящик для записывающей программы почтового "клиента"*/
while (!ShutDown) { /* Цикл выполняется до тех пор, пока имеются серверные потоки. */
/* Ждать, пока другой клиент не откроет почтовый ящик. */
Sleep(CS_TIMEOUT);
hMsFile = CreateFile(MS_CLTNAME, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN EXISTING, FILE ATTRIBUTE NORMAL, NULL);
if (hMsFile == INVALID_HANDLE_VALUE) continue;
/* Отправить сообщение в почтовый ящик. */
MsNotify.msStatus = 0;
MsNotify.msUtilization = 0;
_tcscpy(MsNotify.msName, SERVER_PIPE);
if (WriteFile(hMsFile, &MsNotify, MSM_SIZE, &nXfer, NULL)) ReportError(_T("Ошибка записи почтового сервера."), 13, TRUE);
CloseHandle(hMsFile);
}
_tprintf(_T("Закрытие контролирующего потока.\n"));
_endthreadex(0);
return 0;
}
В программе 11.5 представлена функция, которая вызывается клиентом (см. программу 11.2) для обнаружения сервера.
Программа 11.5. LocSrvr: почтовый сервер
/* Глава 11. LocSrver.c */
/* Найти сервер путем считывания информации из почтового ящика, используемого для широковещательной рассылки имен серверов. */
#include "EvryThng.h"
#include "ClntSrvr.h" /* Определяет имя почтового ящика. */
BOOL LocateServer(LPTSTR pPipeName) {
HANDLE MsFile;
MS_MESSAGE ServerMsg;
BOOL Found = FALSE;
DWORD cbRead;
MsFile = CreateMailslot(MS_SRVNAME, 0, CS_TIMEOUT, NULL);
while (!Found) {
_tprintf(_T("Поиск сервера.\n"));
Found = ReadFile(MsFile, &ServerMsg, MSM_SIZE, &cbRead, NULL);
}
_tprintf(_T("Сервер найден.\n"));
CloseHandle(MsFile);
/* Имя канала сервера. */
_tcscpy(pPipeName, ServerMsg.msName);
return TRUE;
}
Комментарии по поводу многопоточных моделей
Для описания методов проектирования многопоточных программ используются такие термины, как пул потоков (thread pool), симметричные потоки (symmetric threads) иасимметричная потоковая организация программ (asymmetric threading), а мы при создании примеров использовали модель "хозяин/рабочий", именованные каналы и другие классические модели организации многопоточного выполнения программ.
В этом разделе дано краткое объяснение некоторых полезных описательных терминов, которые являются неотъемлемой частью объектно-ориентированной технологии, основанной на разработанной компанией Microsoft модели компонентных объектов (Component Object Model, СОМ; см. [3]): однопоточная модель (single threading), модель апартаментных потоков (apartment model) и модель свободных потоков (free threading). В СОМ эти модели реализуются за счет использования функций Windows, предназначенных для управления потоками и синхронизации их выполнения. Каждая из перечисленных моделей обладает отличными от других характеристиками производительности и предъявляет свои требования к синхронизации.
- Пул потоков -- это совокупность потоков, доступных для использования по мере необходимости. С помощью рис. 7.1 и программы 11.3 иллюстрируется пул потоков, которые могут назначаться новым клиентам, подключающимся к соответствующему именованному каналу. При отсоединении клиента поток возвращается в пул.
- Потоковая модель является симметричной, если группа потоков выполняют одну и ту же задачу с использованием одной и той же функции потока. Симметричные потоки используются в программе grepMT (программа 7.1): все потоки выполняют один и тот же код поиска шаблона. Обратите внимание, что эти потоки не образуют пула; каждый из них создается для выполнения определенной задачи и завершается сразу же после того, как задача выполнена. Пул симметричных потоков создается в программе 11.3.
- Потоковая модель является асимметричной, если различные потоки выполняют различные задачи с использованием различных функций потока. Так, функция потока широковещательной рассылки сообщений, представленная на рис. 7.1 и реализованная в программе 11.4, и функция сервера соответствуют модели асимметричных потоков.
- В соответствии с терминологией СОМ объект является однопоточным, если доступ к нему может получать только один поток. Это означает, что доступ к такому объекту сериализуется. В случае сервера базы данных таким объектом будет сама база данных. В примерах, приведенных в настоящей главе, многопоточная модель используется для организации доступа к объекту, в качестве которого могут рассматриваться программы и файлы, расположенные на компьютере сервера.
- В соответствии с терминологией СОМ об апартаментной модели следует говорить тогда, когда для каждого экземпляра объекта назначается отдельный поток. Так, отдельные потоки могут назначаться для осуществления доступа к разным базам данных или частям базы данных. Доступ к объекту сериализуется с помощью единственного потока.
- Объект, соответствующий модели свободных потоков, имеет поток, который обычно назначается ему из пула потоков по запросу. Обсуждавшийся в настоящей главе сервер можно считать сервером со свободными потоками, если соединение рассматривать как запрос. Аналогично, если сервер базы данных поддерживается потоками, то можно говорить о том, что база данных соответствует модели свободных потоков.
Некоторые программы, например sortMT (программа 7.2), в рамки ни одной из перечисленных моделей точно не укладываются. Вспомните также, что нами уже использовались и другие модели, а именно, модель "хозяин/рабочий", именованные каналы и клиент-серверные модели, применение которых является общепринятым, однако не находит отражения в моделях Microsoft.
Применение указанных моделей многопоточного программирования оказывается уместным и в главе 12, в которой вводятся внутрипроцессные серверы, и компания Microsoft использует соответствующие термины в некоторой части своей документации. Не забывайте о том, что эти термины определены применительно к СОМ-объектам; предыдущее обсуждение показало, как использовать их в более широком контексте. СОМ -- это слишком большой и сложный предмет, чтобы мы могли полностью описать его в данной книге. В списке литературы, приведены некоторые ссылки, которыми вы можете воспользоваться для получения более подробных сведений по этому вопросу.
Резюме
Каналы и почтовые ящики Windows, доступ к которым осуществляется с помощью операций файлового ввода/вывода, обеспечивают поточное межпроцессное и сетевое взаимодействие. В примерах продемонстрировано, как организовать передачу данных из одного процесса в другой при помощи каналов и как построить простую многопоточную клиент-серверную систему. Кроме того, каналы обеспечивают дополнительную возможность синхронизации потоков, поскольку считывающий поток блокируется до тех пор, пока другой поток не выполнит запись в канал.
Размещено на Allbest.ru
...Подобные документы
Потоки и синхронизация действий, выполняемых разными потоками, их методика. Критические секции, их операции и случаи эффективного применения. События и объекты исключительного владения, операции с ними и их сравнение. Функции по работе с глобальной кучей.
лекция [17,6 K], добавлен 24.06.2009Обзор операционных систем, обеспечивающих взаимную синхронизацию процессов и потоков. Понятие критической секции и критических данных, описание приема взаимного исключения. Использование блокирующих переменных и семафоров. Объекты-взаимоисключения.
доклад [26,7 K], добавлен 27.12.2013Функции программного интерфейса операционной системы Windows, предназначенные для работы с семафорами. Средства синхронизации Win32 АРI, основанные на использовании объектов исполнительной системы с дескрипторами. Проблемы при использовании семафоров.
реферат [67,4 K], добавлен 06.10.2010Сущность понятий: "куча" (пул памяти), связный список, синхронизация потоков; разработка программы, исключающей возможность перекрытия потоков друг другом. Организация связных списков и использование функций API для работы с пулом памяти в ОС Windows.
курсовая работа [145,3 K], добавлен 11.05.2012Особенности разработки при использовании потоков. Создание, удаление переменных. Свойства, управление потоками. Вызовы для создания мутекс. Причины завершения потока. Методы синхронизации выполнения потоков. Типичная последовательность действий с мутест.
лекция [160,8 K], добавлен 29.07.2012Общая характеристика потока как последовательности инструкций, которые считывает и исполняет процессор. Анализ преимуществ одно- и многопроцессорности. Особенности реализации потоков и технологий взаимосвязей с микроархитектурой. Средства синхронизации.
курсовая работа [27,4 K], добавлен 18.05.2013Архитектура многопроцессорных систем с общей шиной и с неоднородным доступом к памяти. Структура кэш памяти. Взаимодействие user space с kernel space. Средства синхронизации ядра Linux. Обход каталогов страниц. Инструментация кода средствами Clang.
дипломная работа [513,7 K], добавлен 14.11.2017Основные ограничения синхронизации, необходимые для корректного функционирования системы. Добавление в код производителя и потребителя операторов синхронизации для обеспечения ее корректной работы. Сигнал конечного буфера производителя-потребителя.
курсовая работа [167,0 K], добавлен 05.12.2012Разработка граф-схемы имитационной модели финансовых потоков предприятия и реализация модели программными средствами Pilgrim. Алгоритм моделирования с постоянным шагом. Выполнение моделирования на полученной программе, разработка программного кода.
курсовая работа [1,8 M], добавлен 22.11.2013Описание общего алгоритма и интерфейса программы. Метод заполнения массива случайными числами. Метод вычисления длины линии между пространственными точками. Создание, синхронизация и завершение потоков. TThread как абстрактный класс, листинг программы.
курсовая работа [664,0 K], добавлен 08.04.2014Основные функции и процессы подсистемы управления процессами. Диспетчеризация процессов (потоков). Алгоритмы планирования выполнения потоков. Назначение и разновидности приоритетов в операционных системах. Функции подсистемы управления основной памятью.
презентация [117,7 K], добавлен 20.12.2013Взаимодействие процессов и потоков в операционной системе, основные алгоритмы и механизмы синхронизации. Разработка школьного курса по изучению процессов в операционной системе Windows для 10-11 классов. Методические рекомендации по курсу для учителей.
дипломная работа [3,2 M], добавлен 29.06.2012Классификация компьютерной памяти. Использование оперативной, статической и динамической оперативной памяти. Принцип работы DDR SDRAM. Форматирование магнитных дисков. Основная проблема синхронизации. Теория вычислительных процессов. Адресация памяти.
курсовая работа [1,5 M], добавлен 28.05.2016Количественная, сторона процессов обслуживания потоков сообщений в системах распределения информации. Основные задачи теории телетрафика и сведения о методах решения задач. Принципы классификации потоков вызовов. Просеивание потоков и потоки Эрланга.
реферат [124,6 K], добавлен 18.02.2012Поддержание целостности общих данных, используемые методы и приемы. Проблема критической секции и направления ее разрешения. Аппаратная поддержка синхронизации, классические проблемы и разрешение. Критические области. Синхронизация в Solaris и в Windows.
презентация [1,5 M], добавлен 24.01.2014Обзор средств и методов реализации многопоточности в Java. Проблемы производительности и латентности (времени реакции). Методы использующиеся при работе с потоками. Запуск потоков, завершение процесса и демоны. Взаимная, активная блокировка и голодание.
курсовая работа [275,3 K], добавлен 23.08.2014Общие требования охраны труда во время работы, а также в аварийных ситуациях. Использование метрик программного продукта при ревьюировании. Проверка целостности программного кода и анализ потоков данных. Сценарии использования программного продукта.
отчет по практике [2,0 M], добавлен 28.11.2022Архитектура предприятия как инструмент управления изменениями. Проектирование архитектуры данных по TOGAF. Описание потоков и источников данных. Синхронизация данных по времени. Описание этапов и рекомендации по использованию инструментов проектирования.
дипломная работа [2,8 M], добавлен 09.09.2017Описание основных целей и рабочих процессов оператора сотовой связи. Шкала оценки важности информации. Построение матрицы ответственности за аппаратные ресурсы. Разработка структурной схемы их взаимодействия между собой и модели информационных потоков.
практическая работа [336,0 K], добавлен 28.01.2015Определение наиболее надёжного пути передачи 2-х потоков информации за один цикл между шестью коммутаторами с учётом критерия максимальной помехозащищенности. Вычисление коэффициентов целевой функции и системы ограничений. Оптимальный план обмена данными.
курсовая работа [617,5 K], добавлен 13.01.2015