Составители:
Рубрика:
Последний пример качественно проще, чем пример с явным созда-
нием порта завершения ввода-вывода, хотя часть возможностей порта за-
вершения при этом не может быть использована.
7.1.2. Память, локальная для потоков и волокон
При разработке многопоточных приложений возникает необходи-
мость обеспечивать не только параллельное исполнение кода потоков, но
также их взаимодействие – обмен данными, доступ к общим, разделяе-
мым всеми потоками данным и изоляцию некоторых данных одного пото-
ка от других.
Поскольку все потоки разделяют общее адресное пространство про-
цесса, то все они имеют общий и равноправный доступ ко всем данным,
хранимым в адресном пространстве. Поэтому для потоков, как правило,
не существует проблем с передачей данных друг другу – нужна лишь орга-
низация корректного взаимного доступа и изоляция собственных данных
от данных других потоков.
Очень часто для изоляции данных достаточно их размещать в стеке –
тогда другие потоки смогут получить к ним доступ либо по явно передан-
ным указателям, либо путем сканирования памяти в поисках стеков дру-
гих потоков и нужных данных в этих стеках, что относится уже к достаточ-
но трудоемким хакерским технологиям. Однако таким образом трудно ор-
ганизовать постоянное хранение данных, и необходимо постоянно явным
образом передавать эти данные (или указатель на них) во все вызываемые
процедуры; это достаточно неудобно и не всегда возможно.
Для решения подобных задач в Windows предусмотрен механизм уп-
равления данными, локальными для потока (TLS память, Thread Local
Storage). Система предоставляет небольшой специальный блок данных,
ассоциированный с каждым потоком. В таком блоке возможно в общем
случае хранение произвольных данных, однако, так как размеры этого
блока крайне малы, то обычно там размещаются указатели на данные
большего объема, выделяемые в приложении для каждого потока; в связи
с этим ассоциированную с потоком память можно рассматривать как мас-
сив двойных слов или массив указателей.
ОС Windows предоставляет четыре функции, необходимые для рабо-
ты с локальной для потока памятью. Функция DWORD TlsAlloc(void) выде-
ляет в ассоциированной с потоком памяти двойное слово, индекс которо-
го возвращается вызвавшей процедуре. Если ассоциированный массив
полностью использован, возвращаемое значение будет равно
TLS_OUT_OF_INDEXES, что сообщает об ошибке выделения ячейки. Функция
TlsFree освобождает выделенную ячейку.
Если поток выделил некоторую ячейку в ассоциированном массиве,
то все потоки данного процесса могут обращаться к ячейке с этим индек-
Разработка параллельных приложений для ОС Windows
225
чен для случаев, когда обработка запроса может привести к продолжитель-
ному ожиданию – тогда система может увеличить число потоков в пуле:
#include <process.h>
#define _WIN32_WINNT 0x0500
#include <windows.h>
#define MAXQUERIES 15
#define POOLSIZE 3
static LONG cnt;
static HANDLE hEvent;
DWORD WINAPI QProc( LPVOID lpData )
{
int r = InterlockedIncrement( &cnt );
Sleep( 300 );
if ( r >= MAXQUERIES ) SetEvent( hEvent );
return 0L;
}
int main( void )
{
int i;
hEvent = CreateEvent( NULL, TRUE, FALSE, 0 );
/* в пуле будет не менее POOLSIZE потоков */
for ( i = 0; i < POOLSIZE; i++ ) {
QueueUserWorkItem( QProc, NULL, WT_EXECUTELONGFUNCTION );
Sleep( 60 );
}
/* остальные запросы будут распределяться между потоками
пула,даже если их больше, чем число процессоров */
for ( ; i < MAXQUERIES; i++ ) {
QueueUserWorkItem( QProc, NULL, WT_EXECUTEDEFAULT );
Sleep( 60 );
}
/* со временем система может уменьшить число потоков пула */
/* дожидаемся обработки последнего запроса */
WaitForSingleObject( hEvent, INFINITE );
CloseHandle( hEvent );
return 0;
}
224
CIL и системное программирование в Microsoft .NET
224 CIL и системное программирование в Microsoft .NET Разработка параллельных приложений для ОС Windows 225 чен для случаев, когда обработка запроса может привести к продолжитель- Последний пример качественно проще, чем пример с явным созда- ному ожиданию – тогда система может увеличить число потоков в пуле: нием порта завершения ввода-вывода, хотя часть возможностей порта за- вершения при этом не может быть использована. #include#define _WIN32_WINNT 0x0500 7.1.2. Память, локальная для потоков и волокон #include При разработке многопоточных приложений возникает необходи- мость обеспечивать не только параллельное исполнение кода потоков, но #define MAXQUERIES 15 также их взаимодействие – обмен данными, доступ к общим, разделяе- #define POOLSIZE 3 мым всеми потоками данным и изоляцию некоторых данных одного пото- static LONG cnt; ка от других. static HANDLE hEvent; Поскольку все потоки разделяют общее адресное пространство про- цесса, то все они имеют общий и равноправный доступ ко всем данным, DWORD WINAPI QProc( LPVOID lpData ) хранимым в адресном пространстве. Поэтому для потоков, как правило, { не существует проблем с передачей данных друг другу – нужна лишь орга- int r = InterlockedIncrement( &cnt ); низация корректного взаимного доступа и изоляция собственных данных Sleep( 300 ); от данных других потоков. if ( r >= MAXQUERIES ) SetEvent( hEvent ); Очень часто для изоляции данных достаточно их размещать в стеке – return 0L; тогда другие потоки смогут получить к ним доступ либо по явно передан- } ным указателям, либо путем сканирования памяти в поисках стеков дру- гих потоков и нужных данных в этих стеках, что относится уже к достаточ- int main( void ) но трудоемким хакерским технологиям. Однако таким образом трудно ор- { ганизовать постоянное хранение данных, и необходимо постоянно явным int i; образом передавать эти данные (или указатель на них) во все вызываемые hEvent = CreateEvent( NULL, TRUE, FALSE, 0 ); процедуры; это достаточно неудобно и не всегда возможно. /* в пуле будет не менее POOLSIZE потоков */ Для решения подобных задач в Windows предусмотрен механизм уп- for ( i = 0; i < POOLSIZE; i++ ) { равления данными, локальными для потока (TLS память, Thread Local QueueUserWorkItem( QProc, NULL, WT_EXECUTELONGFUNCTION ); Storage). Система предоставляет небольшой специальный блок данных, Sleep( 60 ); ассоциированный с каждым потоком. В таком блоке возможно в общем } случае хранение произвольных данных, однако, так как размеры этого /* остальные запросы будут распределяться между потоками блока крайне малы, то обычно там размещаются указатели на данные пула,даже если их больше, чем число процессоров */ большего объема, выделяемые в приложении для каждого потока; в связи for ( ; i < MAXQUERIES; i++ ) { с этим ассоциированную с потоком память можно рассматривать как мас- QueueUserWorkItem( QProc, NULL, WT_EXECUTEDEFAULT ); сив двойных слов или массив указателей. Sleep( 60 ); ОС Windows предоставляет четыре функции, необходимые для рабо- } ты с локальной для потока памятью. Функция DWORD TlsAlloc(void) выде- /* со временем система может уменьшить число потоков пула */ ляет в ассоциированной с потоком памяти двойное слово, индекс которо- /* дожидаемся обработки последнего запроса */ го возвращается вызвавшей процедуре. Если ассоциированный массив WaitForSingleObject( hEvent, INFINITE ); полностью использован, возвращаемое значение будет равно CloseHandle( hEvent ); TLS_OUT_OF_INDEXES, что сообщает об ошибке выделения ячейки. Функция return 0; TlsFree освобождает выделенную ячейку. } Если поток выделил некоторую ячейку в ассоциированном массиве, то все потоки данного процесса могут обращаться к ячейке с этим индек-
Страницы
- « первая
- ‹ предыдущая
- …
- 117
- 118
- 119
- 120
- 121
- …
- следующая ›
- последняя »