Common Intermediate Language и системное программирование в Microsoft.Net. Макаров А.В - 119 стр.

UptoLike

Последний пример качественно проще, чем пример с явным созда-
нием порта завершения ввода-вывода, хотя часть возможностей порта за-
вершения при этом не может быть использована.
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 освобождает выделенную ячейку.
      }                                                                                Если поток выделил некоторую ячейку в ассоциированном массиве,
                                                                                  то все потоки данного процесса могут обращаться к ячейке с этим индек-