// подождем, пока все потоки завершатся
WaitForMultipleObjects( iCounter, phThreads, TRUE, THREAD_COUNT*5000 );
DeleteCriticalSection( &g_cs );
// закрываем описатели потоков
// В данном случае это делать необязательно - описатели потоков
// будут закрыты при завершении процесса
while( iCounter > 0 )
CloseHandle( phThreads[--iCounter] );
delete[] phThreads;
return 0;
}
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <stdio.h>
// количество запускаемых потоков
#define THREAD_COUNT 5
// критическая секция (управляющая структура), которая будет защищать разделяемый ресурс - консоль
CRITICAL_SECTION g_cs;
/* БОльшая часть комментариев - цитаты из книги
Рихтер Дж.
"Windows для профессионалов: создание эффективных Win32 приложений с
учетом специфики 64-разрядной версии Windows"
Критическая секция (critical section) — это небольшой участок кода,
требующий монопольного доступа к каким-то общим данным. Она позволяет
сделать так, чтобы единовременно только один поток получал доступ к
определенному ресурсу. Естественно, система может в любой момент
вытеснить Ваш поток и подключить к процессору другой, но ни один из
потоков, которым нужен занятый Вами ресурс, не получит процессорное
время до тех пор, пока Ваш поток не выйдет за границы критической
секции.
Структура CRITICAL_SECTION содержит данные, необходимые для реализации
подобного раздельного доступа. Ее внутренняя организация несущественна для
использования. Прикладные программы не должны обращаться к полям этой
структуры. Вы работаете со структурой CRITICAL_SECTION исключительно
через функции Windows, передавая им адрес соответствующего экземпляра
этой структуры. Функции сами знают, как обращаться с её элементами, и
гарантируют, что она всегда будет в согласованном состоянии.
*/
/*
Вся синхронизация вывода на консоль заключена в функции PrintMsg()
Любой участок кода, работающего с разделяемым ресурсом (здесь - консоль),
нужно заключить в вызовы функций EnterCriticalSection() и LeaveCriticalSection()
Участок кода, работающий с разделяемым ресурсом, предваряется вызовом
EnterCriticalSection()
- Если ресурс свободен, EnterCriticalSection модифицирует элементы
структуры, указывая, что вызывающий поток занимает ресурс, после чего
немедленно возвращает управление, и поток продолжает свою работу
(получив доступ к ресурсу).
- Если значения элементов структуры свидетельствуют, что ресурс уже
захвачен вызывающим потоком, EnterCriticalSection обновляет их,
отмечая тем самым, сколько раз подряд этот поток захватил ресурс, и
немедленно возвращает управление. Такая ситуация бывает нечасто — лишь
тогда, когда поток два раза подряд вызывает EnterCriticalSection без
промежуточного вызова LeaveCriticalSection.
- Если значения элементов структуры указывают на то, что ресурс занят
другим потоком, EnterCriticalSection переводит вызывающий поток в
режим ожидания. Поток, пребывая в ожидании, не тратит ни кванта
процессорного времени. Система запоминает, что данный поток хочет
получить доступ к ресурсу, и - как только поток, занимавший этот
ресурс, вызывает LeaveCriticalSection — вновь начинает выделять нашему
потоку процессорное время. При этом она передает ему ресурс,
автоматически обновляя элементы структуры CRITICAL_SECTION.
В конце участка кода, использующего разделяемый ресурс, должен
присутствовать вызов LeaveCriticalSection.
Эта функция просматривает элементы структуры CRITICAL_SECTION и
уменьшает счетчик числа захватов ресурса вызывающим потоком на 1. Если
его значение больше 0, LeaveCriticalSection ничего не делает и просто
возвращает управление.
Если значение счетчика достигло 0, LeaveCriticalSection сначала
выясняет, есть ли в системе другие потоки, ждущие данный ресурс в
вызове EnterCriticalSection. Если есть хотя бы один такой поток,
функция настраивает значения элементов структуры, что бы они
сигнализировали о занятости ресурса, и отдает его одному из ждущих
потоков (поток выбирается "по справедливости"). Если же ресурс никому
не нужен, Leave CriticalSection соответственно сбрасывает элементы
структуры.
Как и EnterCriticalSection, функция LeaveCriticalSection выполняет все
действия на уровне атомарного доступа. Однако LeaveCriticalSection
никогда не приостанавливает поток, а управление возвращает немедленно.
*/
void PrintMsg( LPSTR lpszFormat, ... )
{
va_list arg;
va_start( arg, lpszFormat );
EnterCriticalSection( &g_cs );
vprintf( lpszFormat, arg );
LeaveCriticalSection( &g_cs );
va_end( arg );
}
DWORD WINAPI ThreadFunc( PVOID pvParam )
{
int index = (int)pvParam;
PrintMsg( "Thread %d started.\n" , index );
Sleep( 5000 );
PrintMsg( "Thread %d finished.\n" , index );
return 0;
}
int main( int /*argc*/, char* /*argv[]*/ )
{
// выделяем память под массив для описателей порожденных потоков
HANDLE* phThreads = new HANDLE[THREAD_COUNT];
if( !phThreads ) return -1;
// критическую секцию следует инициализировать до обращения
// какого-либо потока к защищенному ресурсу
InitializeCriticalSection( &g_cs );
// iCounter - счетчик числа потоков
for( int iCounter = 0; iCounter < THREAD_COUNT; ) {
// создаем новый поток, номер потока передаем как параметр
++iCounter;
DWORD dwThreadId;
// сохраняем описатель, поскольку нам надо дождаться завершения всех
// порожденных потоков
phThreads[iCounter-1] = CreateThread( NULL, 0, ThreadFunc,
(PVOID)iCounter, 0, &dwThreadId );
}
// подождем, пока все потоки завершатся
WaitForMultipleObjects( iCounter, phThreads, TRUE, THREAD_COUNT*5000 );
// критическая секция больше не нужна, освобождаем ресурс
DeleteCriticalSection( &g_cs );
// закрываем описатели потоков
// В данном случае это делать необязательно - описатели потоков
// будут закрыты при завершении процесса
while( iCounter > 0 )
CloseHandle( phThreads[--iCounter] );
// освобождаем память
// В данном случае это делать необязательно - память будет освобождена
// при завершении процесса
delete[] phThreads;
return 0;
}
Если Вы уже зарегистрированы на Портале - войдите в систему, если Вы еще не регистрировались - пройдите простую процедуру регистрации.