Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Решение проблемы долговременных состояний в многопоточной среде




В искомом решении сочетаются несколько компонентов:

• Библиотека DLL, в которой содержатся функции, обеспечивающие отправку и прием сообщений.

• Функция, представляющая точку входа в DLL.

• Локальная область хранения потока (TLS, глава 7). Подключение процесса к библиотеке сопровождается созданием индекса DLL, а отключение — уничтожением. Значение индекса хранится в статическом хранилище, доступ к которому имеют все потоки.

• Структура, в которой хранится буфер и его текущее состояние. Структура распределяется всякий раз, когда к библиотеке подключается новый поток, и его адрес сохраняется в записи TLS для данного потока. При отсоединении потока от библиотеки память, занимаемая его структурой, освобождается.

Таким образом, TLS играет роль статического хранилища, и у каждого потока имеется собственная уникальная копия этого хранилища.

Пример: безопасная многопоточная DLL для обмена сообщениями через сокет

Программа 12.4 представляет собой DLL, содержащую две функции для обработки символьных строк (в именах которых в данном случае присутствует "CS", от character string — строка символов), или потоковые функции сокета (socket streaming functions): SendCSMessage и ReceiveCSMessage, а также точку входа DllMain (см. главу 5). Указанные две функции играют ту же роль, что и функция ReceiveMessage, а также функции, использованные в программах 12.1 и 12.2, и фактически заменяют их.

Функция DllMain служит характерным примером решения проблемы долговременных состояний в многопоточной среде и объединяет TLS и библиотеки DLL.

Освобождать ресурсы при отсоединении потоков (случай DLL_THREAD_DETACH) особенно важно в случае серверной среды; если этого не делать, то ресурсы сервера, в конечном счете, исчерпаются, что может привести к сбоям в его работе или снижению производительности или к тому и другому одновременно.

Примечание

Некоторые из иллюстрируемых ниже концепций прямого отношения к сокетам не имеют, но, тем не менее, рассматриваются именно здесь, а не в предыдущих главах, поскольку данный пример предоставляет удобную возможность для иллюстрации методов создания безопасных многопоточных DLL в реалистических условиях.

Использующие эту DLL коды клиента и сервера, незначительно измененные по сравнению с программами 12.1 и 12.2, доступны на Web-сайте книги.

Программа 12.4. SendReceiveSKST: безопасная многопоточная DLL

/* SendReceiveSKST.с — DLL многопоточного потокового сокета. */

/* В качестве разделителей сообщений используются символы конца */

/* строки ('\0'), так что размер сообщения заранее не известен. */

/* Поступающие данные буферизуются и сохраняются в промежутках между */

/* вызовами функций. */

/* Для этой цели используются локальные области хранения потоков */

/* (Thread Local Storage, TLS), обеспечивающие каждый из потоков */

/* собственным закрытым "статическим хранилищем". */

 

#define _NOEXCLUSIONS

#include "EvryThng.h"

#include "ClntSrvr.h" /* Определяет записи запроса и ответа. */

 

typedef struct STATIC_BUF_T {

/* "static_buf" содержит "static_buf_len" байтов остаточных данных. */

/* Символы конца строки (нулевые символы) могут присутствовать, а могут */

/* и не присутствовать. */

char static_buf[MAX_RQRS_LEN];

LONG32 static_buf_len;

} STATIC_BUF;

 

static DWORD TlsIx = 0; /* Индекс TLS – ДЛЯ КАЖДОГО ПРОЦЕССА СВОЙ ИНДЕКС.*/

/* Для однопоточной библиотеки использовались бы следующие определения:

static char static_buf [MAX_RQRS_LEN];

static LONG32 static_buf_len; */

/* Основная функция DLL. */

 

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {

STATIC_BUF * pBuf;

switch (fdwReason) {

case DLL_PROCESS_ATTACH:

TlsIx = TlsAlloc();

/* Для основного потока подключение отсутствует, поэтому во время подключения процесса необходимо выполнить также операции по подключению потока. */

case DLL_THREAD_ATTACH:

/* Указать, что память не была распределена. */

TlsSetValue(TlsIx, NULL);

return TRUE; /* В действительности это значение игнорируется. */

case DLL_PROCESS_DETACH:

/* Отсоединить также основной поток. */

pBuf = TlsGetValue(TlsIx);

if (pBuf!= NULL) {

free(pBuf);

pBuf = NULL;

}

return TRUE;

case DLL_THREAD_DETACH:

pBuf = TlsGetValue(TlsIx);

if (pBuf!= NULL) {

free(pBuf);

pBuf = NULL;

}

return TRUE;

}

}

 

_declspec(dllexport)

BOOL ReceiveCSMessage(REQUEST *pRequest, SOCKET sd) {

/* Возвращаемое значение TRUE указывает на ошибку или отсоединение. */

BOOL Disconnect = FALSE;

LONG32 nRemainRecv = 0, nXfer, k; /* Должны быть целыми со знаком. */

LPSTR pBuffer, message;

CHAR TempBuf[MAX_RQRS_LEN + 1];

STATIC_BUF *p;

p = (STATIC_BUF *)TlsGetValue(TlsIx);

if (p == NULL) { /* Инициализация при первом вызове. */

/* Распределять это хранилище будут только те потоки, которым оно */

/* необходимо. Другие типы потоков могут использовать TLS для иных целей. */

р = malloc(sizeof(STATIC_BUF));

TlsSetValue(TlsIx, p);

if (p == NULL) return TRUE; /* Ошибка. */

p->static_buf_len = 0; /* Инициализировать состояние. */

}

message = pRequest->Record;

/* Считать до символа новой строки, оставляя остаточные данные в статическом буфере. */

for (k = 0; k < p->static_buf_len && p->static_buf[k]!= '\0'; k++) {

message[k] = p->static_buf[k];

} /* k – количество переданных символов. */

if (k < p->static_buf_len) { /* В статическом буфере обнаружен нулевой символ. */

message[k] = '\0';

p->static_buf_len –= (k + 1); /* Скорректировать состояние статического буфера. */

memcpy(p->static_buf, &(p->static_buf[k + 1]), p->static_buf_len);

return FALSE; /* Входные данные сокета не требуются. */

}

 

/* Передан весь статический буфер. Признак конца строки не обнаружен.*/

nRemainRecv = sizeof(TempBuf) – 1 – p->static_buf_len;

pBuffer = message + p->static_buf_len;

p->static_buf_len = 0;

while (nRemainRecv > 0 &&!Disconnect) {

nXfer = recv(sd, TempBuf, nRemainRecv, 0);

if (nXfer <= 0) {

Disconnect = TRUE;

continue;

}

nRemainRecv –= nXfer;

/* Передать в целевое сообщение все символы вплоть до нулевого, если таковой имеется. */

for (k =0; k < nXfer && TempBuf[k]!= '\0'; k++) {

*pBuffer = TempBuf[k];

pBuffer++;

}

if (k >= nXfer) { /*Признак конца строки не обнаружен, читать дальше*/

nRemainRecv –= nXfer;

} else { /* Обнаружен признак конца строки. */

*pBuffer = '\0';

nRemainRecv = 0;

memcpy(p->static_buf, &TempBuf[k + 1], nXfer – k – 1);

p->static_buf_len = nXfer – k – 1;

}

}

return Disconnect;

}

 

_declspec(dllexport)

BOOL SendCSMessage(RESPONSE *pResponse, SOCKET sd) {

/* Послать запрос серверу в сокет sd. */

BOOL Disconnect = FALSE;

LONG32 nRemainSend, nXfer;

LPSTR pBuffer;

pBuffer = pResponse->Record;

nRemainSend = strlen(pBuffer) + 1;

while (nRemainSend > 0 &&!Disconnect) {

/* Отправка еще не гарантирует, что будет отослано все сообщение. */

nXfer = send(sd, pBuffer, nRemainSend, 0);

if (nXfer <= 0) {

fprintf(stderr, "\nОтключение сервера до посылки запроса завершения");

Disconnect = TRUE;

}

nRemainSend –=nXfer;

pBuffer += nXfer;

}

return Disconnect;

}





Поделиться с друзьями:


Дата добавления: 2015-09-20; Мы поможем в написании ваших работ!; просмотров: 499 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Слабые люди всю жизнь стараются быть не хуже других. Сильным во что бы то ни стало нужно стать лучше всех. © Борис Акунин
==> читать все изречения...

2240 - | 2159 -


© 2015-2025 lektsii.org - Контакты - Последнее добавление

Ген: 0.011 с.