Составители:
Рубрика:
вот те данные, указатели на которые хранились в TLS памяти, –
нет. Необходимо специально отслеживать все возможные слу-
чаи завершения потоков, включая завершение по ошибке, и
принимать меры для освобождения выделенной памяти. При
использовании _declspec(thread) эта ситуация встречается ре-
же, так как позволяет хранить в _TLS сегментах данные любого
фиксированного размера.
Следует отметить еще один нюанс, связанный с использованием TLS
памяти, волокон и оптимизации. В частных случаях волокна могут испол-
няться разными потоками – при этом одно и то же волокно должно иметь
доступ к TLS памяти именно того потока, в котором оно в данный момент
исполняется. А если компилятор генерирует оптимизированный код, то
он может разместить указатель на данные TLS памяти в каком-либо реги-
стре или временной переменной, что при переключении волокна на дру-
гой поток приведет к ошибке – будет использована TLS память предыду-
щего потока. Чтобы избежать такой ситуации, компилятору можно ука-
зать специальный ключ /GT, отключающий некоторые виды оптимизации
при работе с TLS памятью. Это может потребоваться в крайне редких слу-
чаях – когда приложение использует несколько волокон, исполняемых в
нескольких потоках, и при этом волокна должны использовать TLS па-
мять потоков.
Аналогично TLS памяти, Windows поддерживает память, локальную
для волокон, – так называемую FLS память, или Fiber Local Storage. При
этом FLS память не зависит от того, какой именно поток выполняет дан-
ную нить. Для работы с FLS памятью Windows предоставляет набор функ-
ций, аналогичный Tls-функциям, отличие заключается только в функции
выделения ячейки FLS памяти:
DWORD FlsAlloc( PFLS_CALLBACK_FUNCTION lpCallback );
VOID WINAPI FlsCallback( PVOID lpFlsData )
{
...
}
Функция отличается от ее аналога TlsAlloc указателем на специаль-
ную необязательную процедуру FlsCallback, предоставляемую разработ-
чиком. Эта процедура будет вызвана автоматически при освобождении
ячейки FLS памяти (как при завершении волокна, так и при завершении
потока или возникновении ошибки), и разработчик может легко предос-
тавить средства для освобождения памяти, указатели на которую были со-
хранены в ячейках FLS памяти.
Разработка параллельных приложений для ОС Windows
229
int ProcB( void )
{
int i, x;
for ( i = x = 0; i < 100; i++ ) x += iptr[i];
return x;
}
unsigned __stdcall ThreadProc( void *param )
{
ProcA( (int)param );
Sleep( 0 );
if ( ProcB() != 100*(int)param ) { /* ОШИБКА!!! */ }
return 0;
}
int main( void )
{
HANDLE hThread[THREADS];
unsigned dwThread;
int i;
/* создаем новые потоки */
for ( i = 0; i < THREADS; i++ )
hThread[i] = (HANDLE)_beginthreadex(
NULL, 0, ThreadProc, (void*)i, 0, &dwThread
);
/* дождаться завершения созданных потоков */
WaitForMultipleObjects( THREADS, hThread, TRUE, INFINITE );
for ( i = 0; i < THREADS; i++ ) CloseHandle( hThread[i] );
TlsFree( dwTlsData );
return 0;
}
Следует внимательно следить за выделением и освобождением дан-
ных, указатели на которые сохраняются в TLS памяти (как в случае явно-
го управления, так и при использовании _declspec(thread)). Могут воз-
никнуть две потенциально ошибочных ситуации:
1. TLS память резервируется в то время, когда уже существуют по-
токи. Это возможно при явном управлении TLS памятью, и для
существующих потоков будут зарезервированы ячейки, но при-
дется предусмотреть специальные меры для их корректной ини-
циализации или для исключения их использования до этого.
2. Все случаи завершения потока. Если TLS память содержит ка-
кие-либо указатели, то сама TLS память будет освобождена, а
228
CIL и системное программирование в Microsoft .NET
228 CIL и системное программирование в Microsoft .NET Разработка параллельных приложений для ОС Windows 229 int ProcB( void ) вот те данные, указатели на которые хранились в TLS памяти, – { нет. Необходимо специально отслеживать все возможные слу- int i, x; чаи завершения потоков, включая завершение по ошибке, и for ( i = x = 0; i < 100; i++ ) x += iptr[i]; принимать меры для освобождения выделенной памяти. При return x; использовании _declspec(thread) эта ситуация встречается ре- } же, так как позволяет хранить в _TLS сегментах данные любого фиксированного размера. unsigned __stdcall ThreadProc( void *param ) Следует отметить еще один нюанс, связанный с использованием TLS { памяти, волокон и оптимизации. В частных случаях волокна могут испол- ProcA( (int)param ); няться разными потоками – при этом одно и то же волокно должно иметь Sleep( 0 ); доступ к TLS памяти именно того потока, в котором оно в данный момент if ( ProcB() != 100*(int)param ) { /* ОШИБКА!!! */ } исполняется. А если компилятор генерирует оптимизированный код, то return 0; он может разместить указатель на данные TLS памяти в каком-либо реги- } стре или временной переменной, что при переключении волокна на дру- гой поток приведет к ошибке – будет использована TLS память предыду- int main( void ) щего потока. Чтобы избежать такой ситуации, компилятору можно ука- { зать специальный ключ /GT, отключающий некоторые виды оптимизации HANDLE hThread[THREADS]; при работе с TLS памятью. Это может потребоваться в крайне редких слу- unsigned dwThread; чаях – когда приложение использует несколько волокон, исполняемых в int i; нескольких потоках, и при этом волокна должны использовать TLS па- /* создаем новые потоки */ мять потоков. for ( i = 0; i < THREADS; i++ ) Аналогично TLS памяти, Windows поддерживает память, локальную hThread[i] = (HANDLE)_beginthreadex( для волокон, – так называемую FLS память, или Fiber Local Storage. При NULL, 0, ThreadProc, (void*)i, 0, &dwThread этом FLS память не зависит от того, какой именно поток выполняет дан- ); ную нить. Для работы с FLS памятью Windows предоставляет набор функ- /* дождаться завершения созданных потоков */ ций, аналогичный Tls-функциям, отличие заключается только в функции WaitForMultipleObjects( THREADS, hThread, TRUE, INFINITE ); выделения ячейки FLS памяти: for ( i = 0; i < THREADS; i++ ) CloseHandle( hThread[i] ); TlsFree( dwTlsData ); DWORD FlsAlloc( PFLS_CALLBACK_FUNCTION lpCallback ); return 0; } VOID WINAPI FlsCallback( PVOID lpFlsData ) Следует внимательно следить за выделением и освобождением дан- { ных, указатели на которые сохраняются в TLS памяти (как в случае явно- ... го управления, так и при использовании _declspec(thread)). Могут воз- } никнуть две потенциально ошибочных ситуации: Функция отличается от ее аналога TlsAlloc указателем на специаль- 1. TLS память резервируется в то время, когда уже существуют по- ную необязательную процедуру FlsCallback, предоставляемую разработ- токи. Это возможно при явном управлении TLS памятью, и для чиком. Эта процедура будет вызвана автоматически при освобождении существующих потоков будут зарезервированы ячейки, но при- ячейки FLS памяти (как при завершении волокна, так и при завершении дется предусмотреть специальные меры для их корректной ини- потока или возникновении ошибки), и разработчик может легко предос- циализации или для исключения их использования до этого. тавить средства для освобождения памяти, указатели на которую были со- 2. Все случаи завершения потока. Если TLS память содержит ка- хранены в ячейках FLS памяти. кие-либо указатели, то сама TLS память будет освобождена, а
Страницы
- « первая
- ‹ предыдущая
- …
- 119
- 120
- 121
- 122
- 123
- …
- следующая ›
- последняя »