THREAD_PRIORITY_ABOVE_NORMAL




цесса

THREAD_PRIORITY_HIGHEST

са

THREAD_PRIORITY_TIME_CRITICAL

ских процессов)

THREAD_PRIORITY_IDLE

ских процессов)


Тот же уровень приоритета, что и у про-

 

На один уровень выше приоритета про-

 

На два уровня выше приоритета процес-

 

Уровень 15 (для обычных пользователь-

 

Уровень 1 (для обычных пользователь-


 

Прерванный поток приостанавливает свое выполнение и не учитывается при рас-

пределении времени центрального процессора. Поток остается в таком состоянии до тех

пор, пока другой поток не возобновит его выполнение. Остановку потока можно произ-

вести, в частности, в том случае, если пользователь прерывает выполнение определен-

ной задачи. До тех пор, пока задание не будет отменено, поток можно перевести в со-

стояние ожидания. Если пользователь решит продолжить работу, поток возобновит

выполнение с той точки, где он был остановлен. Для приостановки и возобновления вы-

полнения потоков служат функции

 

DWORD SuspendThread (HANDLE hThread);

DWORD ResumeThread(HANDLE hThread);

 

Один и тот же поток можно последовательно остановить несколько раз, не возоб-

новляя его выполнения, однако каждой последовательной команде SuspendThread

должна соответствовать ответная команда ResumeThread. Система отчитывает количе-

ство отмененных команд с помощью счетчика прерываний. Каждая команда Suspend-

Thread инкрементирует значения счетчика, а каждая команда ResumeThread декремен-

тирует его. Обе функции возвращают предыдущее значение счетчика в виде параметра

типа DWORD. Поток возобновит свое выполнение только в том случае, если счетчик

примет значение 0.

Поток способен остановить себя, но он не в состоянии самостоятельно возобновить

свое выполнение. Однако он может на нужное время перенести себя в режим ожидание.

Команда Sleep задерживает выполнения потока, удаляя его из очереди программы-

планировщика до тех пор, пока не пройдет заданный интервал времени. Интерактивные

потоки, которые выводят определенную информацию для пользователя, часто делают

короткие паузы, чтобы дать ему время для ознакомления с результатами. Применения

режима ожидания предпочтительнее задействования "пустого" цикла, поскольку в этом

случае не используется время центрального процессора.

Для осуществления паузы в течение заданного времени поток вызывает следую-

щие функции:

 

VOID Sleep (DWORD dwMilliseconds);


DWORD SleepEx(DWORD dwMilliseconds,

BOOL bAlertable);


// продолжительность паузы

// TRUE - возобновить работу


// при завершении операции ввода/вывода

 



 

 

Расширенная функция SleepEx обычно работает совместно с функциями фонового

ввода/вывода и может использоваться для инициации команд чтения или записи, не тре-

буя их завершения. Эти операции выполняются в фоновом режиме. По завершении опе-

рации система извещает об этом пользователя, обращаясь к предусмотренной в про-

грамме процедуре обратного вызова. Фоновый ввод/вывод (или перекрывающийся

ввод/вывод далее будет рассмотрен более подробно) чаще всего применяется в интерак-

тивных программах, которые должны реагировать на команды пользователя, не преры-

вая работы со сравнительно медленными устройствами, например с накопителем на

магнитной ленте или с сетевым диском.

Параметр bAlertable функции SleepEx имеет тип Boolean. Если этот параметр равен

TRUE, система может преждевременно возобновить выполнение потока при условии,

что перекрывающаяся операция ввода/вывода завершилась до истечения заданного вре-

мени ожидания. В случае досрочного прерывания функция sleepEx возвращает значение

WAIT_IO_COMPLETION, по истечении указанного времени - значение 0.

По специальному запросу поток возвращает свои дескриптор и идентификатор.

Указанные ниже функции позволяют получить информацию о текущем потоке:

 

DWORD GetCurrentThreadId(VOID);

HANDLE GetCurrentThread(VOID);

 

Результирующее значение функции GetCurrentThreadId совпадает со значением па-

раметра lpIDThread после выполнения функции CreateThread и однозначно идентифици-

рует поток в системе. Хотя идентификатор потока нужен лишь для незначительного ко-

личества функций Win32 API, он может использоваться с целью мониторинга

системных потоков без необходимости поддерживать открытый дескриптор для каждого

потока. Открытый дескриптор защищает поток от уничтожения.

Дескриптор, полученный в результате выполнения функции GetCurrentThread,

служит для тех же целей, что и дескриптор, возвращенный функцией CreateThread. И

несмотря на то, что он может использоваться аналогично другим дескрипторам, на са-

мом деле этот параметр является псевдодескриптором. Псевдодескриптор - это специ-

альная константа, которая всегда интерпретируется системой особым образом, подобно

тому, как одиночная точка (.) в DOS всегда указывает на текущий каталог, а параметр

this в C++ определяет текущий объект. Константа - псевдодескриптор, полученная в ре-

зультате выполнения функции GetCurrentThread, указывает на текущий поток. В отличие

от настоящих дескрипторов, псевдодескриптор не может передаваться другим потокам.

Чтобы получить настоящий переносимый дескриптор потока, необходимо выполнить

следующие действия:

 

HANDLE hThread;

hThread = DuplicateHandle(


GetCurrentProcess(),

GetCurrentThread(),

GetCurrentProcess(),

&hThread,

0,

 

FALSE,

 

DUPLICATE_SAME_ACCESS);


 

 


// процесс-источник

// исходный дескриптор

// целевой процесс

// новый дублирующийся дескриптор

//привилегии доступа (подавляемые

// последним параметром)

// дочерние объекты не унаследуют

// дескриптор

// привилегии доступа копируются у

// исходного дескриптора



 

 

Хотя функция CloseHandle не влияет на псевдодескрипторы, дескриптор, созданный с

помощью функции DuplicateHandle, является настоящим и в конце концов должен быть

закрыт. Применение псевдодескрипторов значительно ускоряет работу функции

GetCurrentThread, поскольку в этом случае подразумевается, что поток имеет полный

доступ сам к себе, и функция возвращает результат, не заботясь о мерах безопасности.

По аналогии с тем, как Windows-программа завершается по достижении конца

функции WinMain, поток обычно прекращает свое существование при достижении кон-

ца функции, в которой был начат. Когда он достигает конца стартовой функции, система

автоматически вызывает команду ExitThread, имеющую такой синтаксис:

 

VOID ExitThread(DWORD dwExitCode);

 

Хотя операционная система вызывает функцию ExitThread автоматически, при не-

обходимости досрочного завершения потока вы можете вызвать эту функцию явным об-

разом:

 

DWORD ThreadFunction(LPDWORD lpdwParam)

{

HANDLE hThread = CreateThread(<параметры>);

// далее следуют стандартные операции инициализации;

// проверка наличия ошибочных условий

if (<условие возникновения ошибки>)

{

ExitThread(ERROR_CODE); // прекратить работу потока

}

// ошибки нет, работа продолжается

return(SUCCESS_CODE ); // эта строка программы

// заставляет систему вызвать функцию ExitThread

}

 

Параметры ERROR_CODE и SUCCESS_CODE определяются по вашему усмотре-

нию. В нашем простом примере поток нетрудно прервать с помощью команды return:

 

if(<условие возникновения ошибки>)

{

return(ERROR_CODE); // прекратить работу потока

}

 

В данном случае, команда return приводит к тому же результату, что и функция Ex-

itThread, так как при ее выполнении осуществляется неявный вызов последней. Эта

функция особенно полезна при необходимости прервать поток из любой под програм-

мы, вызываемой внутри функции типа ThreadFunction.

Когда поток завершается с помощью оператора return, 32-разрядный код заверше-

ния автоматически передается функции ExitThread. После прекращения работы потока

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

 

// один поток вызывает эту функцию для получения кода завершения другого потока

BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpdwExitCode);

 

Функция GetExitCodeThread возвращает значение FALSE в том случае, если опре-

делить код завершения помешала ошибка.

 

 


 

 

Независимо от того, как - явно или неявно (в результате выполнения оператора re-

turn) - вызывается функция ExitThread, она удаляет поток из очереди программы-

планировщика и уничтожает его стек. Однако сам объект при этом сохраняется. Поэто-

му даже после прекращения выполнения потока вы можете запросить его код заверше-

ния. По возможности дескриптор потока следует закрывать явно (с помощью функций

CloseHandle), с тем чтобы поток не занимал лишний объем памяти. При закрытии по-

следнего дескриптора система автоматически уничтожает поток. Система не может

уничтожить выполняющийся поток, даже если закрыты все его дескрипторы. В этом

случае поток будет уничтожен сразу же после завершения выполнения. Если по завер-

шении процесса остаются незакрытые дескрипторы, система закрывает их автоматиче-

ски и удаляет все "подвешенные" объекты, которые не принадлежат ни одному процес-

су.

С помощью команды ExitThread поток может остановить себя самостоятельно в

том месте программы, где это необходимо. Кроме того, один поток способен по своему

усмотрению мгновенно остановить другой поток.

 

// с помощью вызова этой функции один поток может остановить другой

BOOL TerminateThread (HANDLE hThread, DWORD dwExitCode);

 

Поток не в состоянии защитить себя от прерывания. Имея соответствующий деск-

риптор, любой объект может мгновенно остановить поток вне зависимости от его теку-

щего состояния (конечно, в том случае; если дескриптор разрешает полный доступ к по-

току). Если при вызове функции CreateThread использовать набор атрибутов

безопасности, заданный по умолчанию, то результирующий дескриптор обеспечит пол-

ные привилегии доступа к созданному потоку.

Функция TerminateThread не уничтожает стек потока, а только возвращает код его

завершения. Функции ExitThread и TerminateThread переводят объект в сигнальное со-

стояние, что служит признаком возможности запуска других потоков, ожидавших его

завершения. После выполнения любой из двух указанных функций поток продолжает

существовать в сигнальном состоянии до тех пор, пока не будут закрыты все его деск-

рипторы.

 

2.2.3. Синхронизация потоков

 

При работе с потоками необходимо иметь возможность координировать их дейст-

вия. Часто координация действий подразумевает определенный порядок выполнения

операций. Кроме функций, предназначенных для создания потоков и изменения их пла-

нового приоритета, Win32 API содержит функции, которые переводят потоки в режим

ожидания сигналов от определенных объектов, например от файлов или процессов.

Кроме того, эти функции обеспечивают поддержку некоторых специальных объектов, в

частности семафоров и исключающих семафоров.

Лучше всего проиллюстрировать применение синхронизирующих объектов можно

на примере функций, ожидающих сигнала от объекта. С помощью одного набора обоб-

щенных команд можно организовать ожидание сигналов от процессов, семафоров, ис-

ключающих семафоров, событий и некоторых других объектов. Следующая функция

ожидает поступления сигнала от указанного объекта:


 

 

DWORD WaitForSingleObject(HANDLE hObject,

 

DWORD dwMilliseconds);

 

 


 

 

// объект, сигнал от

// которого ожидается

// максимальное время ожидания



 

Функция WaitForSingleObject позволяет приостановить выполнение потока до тех

пор, пока не поступит сигнал от заданного объекта. Кроме того, в этой команде указыва-

ется максимальное время ожидания. Чтобы обеспечить бесконечное ожидание, в качест-

ве временного интервала следует задать значение INFINITE. Если объект уже доступен

или если он подает сигнал в течение заданного времени, функция WaitForSingleObject

возвращает значение 0 и выполнение потока возобновляется. Но если заданный интер-

вал времени прошел, а объект не подал сигнала, функция возвращает значение

WAIT_TIMEOUT.

Для того чтобы заставить поток ожидать сигналы сразу от нескольких объектов,

воспользуйтесь функцией WaitForMultipleObjects. Функция возвратит управление пото-

ку при поступлении сигнала либо от одного из указанных объектов, либо от всех объек-

тов вместе. В программе, управляемой событиями, должен быть задан массив объектов.

 

DWORD WaitForMultipleObjects(


DWORD dwNumObjects,

LPHANDLE lpHandles,

BOOL bWaitAll,

 

 

DWORD dwMilliseconds);


// количество ожидаемых объектов

// массив дескрипторов

// TRUE - ожидание сигналов

// сразу от всех объектов;

// FALSE - ожидание сигнала от

// любого из объектов

// максимальный период ожидания


 

Результирующее значение WAIT_TIMEOUT, опять-таки, говорит о том, что задан-

ный интервал времени прошел, а сигнал от объектов не поступил. Если флаг bWaitAll

имеет значение FALSE, соответствующее ожиданию сигнала от любого из указанных

объектов, в случае успешного завершения функция WaitForMultipleObjects возвращает

код, который указывает, от какого из элементов массива lpHandles поступил сигнал.

(Первый элемент массива соответствует значению 0, второй - значению 1 и т.д.). Если

флаг bWaitАll имеет значение TRUE, функция не возвращает результат до тех пор, пока

не будут установлены флаги всех объектов (т.е. пока не завершите выполнение всех по-

токов).

Две расширенные версии функций ожидания содержат дополнительный флаг ста-

туса оповещения, который позволяет возобновить выполнение потока, если в течение

периода ожидания были завершены асинхронные операции чтения или записи. Работу

этих функций можно представить так, как будто они просят "разбудить" их в одном из

трех случаев: если становится доступным указанный объект, если заканчивается задан-

ный период времени или если завершилось выполнение фоновой операции вво-

да/вывода.

 

DWORD WaitForSingleObjectEx (


HANDLE hObject,

DWORD dwMilliseconds,

BOOL bAlertable);

 

DWORD WaitForMultipleObjectsEx(

DWORD dwNumObjects,

LPHANDLE lpHandles,

BOOL bWaitAll,


// объект, сигнал от которого ожидается

// максимальное время ожидания

// TRUE - прекращение ожидания

// при завершении операции ввода/вывода

 

// количество ожидаемых объектов

// массив дескрипторов

// TRUE - ожидание сигналов

// сразу от всех объектов;

// FALSE - ожидание сигнала от

 

 



 

DWORD dwMilliseconds,

BOOL bAlertable);


 

 

// любого из объектов

// максимальный период ожидания

// TRUE - прекращение ожидания

// при завершении операции ввода/вывода


 

При успешном выполнении функции ожидания объект, сигнал от которого ожи-

дался, обычно определенным образом изменяется. Например, если поток ожидал и по-

лучил сигнал от исключающего семафора, функция восстанавливает несигнальное со-

стояние исключающего семафора, чтобы остальные потоки знали о том, что он занят.

Кроме того, функции ожидания декрементируют значение счетчика семафора и сбрасы-

вают информацию о некоторых событиях.

Функции ожидания не изменяют состояния указанного объекта до тех пор, пока не

поступит сигнал от одного или нескольких других объектов. В частности, поток не за-

хватывает исключающий семафор сразу же после поступления сигнала от него, а ожида-

ет сигналов от других объектов. Кроме того, в течение времени ожидания исключающий

семафор снова может быть захвачен другим потоком, который еще больше продлит со-

стояние ожидания.

Конечно, ожидать поступления сигнала от объекта можно лишь в том случае, если

этот объект уже создан. Начнем с создания исключающихсемафоров и семафоров, по-

скольку для работы с ними существуют параллельные API-команды, позволяющие соз-

давать и уничтожать эти объекты, захватывать их и освобождать, а также получать их

дескрипторы.

Функциям, создающим исключающиесемафоры и семафоры, нужно указать тре-

буемые привилегии доступа и начальные параметры создаваемого объекта (можно также

указать его имя, но это необязательно) [12].

 

HANDLE CreateMutex (


LPSECURITY_ATTRIBUTES lpsa,

сти


// необязательные атрибуты безопасно-


BOOL bInitialOwner

 

LPTSTR lpszMutexName)

 

HANDLE CreateSemaphore(


// TRUE - создатель хочет

// завладеть полученным объектом

// имя объекта


LPSECURITY_ATTRIBUTES lpsa,


//необязательные атрибуты безопасности


LONG lInitialCount,

LONG lMaxCount,

 

LPTSTR lpszSemName);


// исходное значение счетчика (обычно 0)

// максимальное значение

// счетчика (ограничивает число потоков)

// имя семафора (может иметь значение NULL)


 

Если в качестве атрибута безопасности задано значение NULL, результирующий

дескриптор получит все привилегии доступа и не будет наследоваться дочерними про-

цессами. Имена объектов являются необязательными, однако они становятся полезными

в ситуации, когда несколько процессов управляют одним и тем же объектом.

Если флагу bInitialOwner присвоить значение TRUE, поток сразу после создания

объекта завладеет им. Созданный исключающий семафор не станет подавать сигналы до

тех пор, пока поток не освободит его.

В отличие от исключающего семафора, который может принадлежать только од-

ному потоку, неисключающий семафор остается в сигнальном состоянии до тех пор, по-

ка его счетчик захватов не получит значения iMaxCount. Если другие потоки в этот мо-

 



 

 

мент попытаются завладеть семафором, они будут приостановлены до тех пор, пока

счетчик захватов не будет декрементирован до значения ниже максимального.

Пока семафор (или исключающий семафор) существует, поток взаимодействует с

ним посредством операций захвата и освобождения. Для захвата любого объекта поток

вызывает функцию WaitForSingleObject (или одну из ее разновидностей). Завершив вы-

полнение задачи, которая синхронизировалась захваченным объектом, поток освобож-

дает этот объект с помощью одной из следующих функций:

 

BOOL ReleaseMutex(HANDLE hMutex);

 

BOOL ReleaseSemaphore(

HANDLE hSemaphore,


LONG lRelease,

 

 

LPLONG lplPrevious);


// величина, на которую

// инкрементируется значение счетчика

// при освобождении объекта (обычно 1)

// переменная, которой присваивается

// предыдущее значение счетчика


 

При освобождении семафора: или исключающего семафора значение счетчика за-

хватов инкрементируется. Значение счетчика, превышающее 0, воспринимается систе-

мой как сигнал объекта ожидающим его потокам.

Освободить исключающий семафор может только тот поток, который завладел им.

Однако любой поток может вызвать функцию ReleaseSemaphore, которая инкрементиру-

ет значение счетчика захватов обычного семафора вплоть до его максимального значе-

ния. Изменение значения счетчика дает возможность в процессе выполнения программы

произвольным образом задать количество потоков, которые могут завладеть семафором.

Обратите внимание, что функция CreateSemaphore позволяет при создании нового сема-

фора присвоить его счетчику значение, меньшее максимального. Например, при разра-

ботке нового семафора его счетчику можно задать начальное значение 0. Такой прием

позволит заблокировать все потоки до тех пор, пока программа не произведет инициали-

зацию, а затем не увеличит значение счетчика с помощью команды ReleaseSemaphore.

Не забывайте вовремя освобождать синхронизирующие объекты. Не задав макси-

мального времени ожидания и забыв освободить исключающий семафор, вы заблоки-

руете все ожидающие его потоки.

Поток может ожидать несколько сигналов от одного и того же объекта, не будучи

заблокированным, однако после завершения каждого из процессов ожидания необходи-

мо, выполнять операцию освобождения. Это требование справедливо для семафоров,

исключающих семафоров и критических разделов.

Событие представляет собой объект, который создается программой при необхо-

димости информировать потоки о выполнении определенных действий. В простейшем

случае (ручной сброс) событие переключает свое состояние с помощью команд SetEvent

(сигнал включен) и ResetEvent (сигнал выключен). Когда сигнал включается, его полу-

чают все потоки, которые ожидают появления соответствующего события. Если сигнал

выключается, все такие потоки блокируются. В отличие от семафоров и исключающих

семафоров, события данного типа изменяют свое состояние только при подаче соответ-

ствующей команды каким-нибудь потоком.

События целесообразно использовать при условии, что поток должен выполняться

только после того, как программа обновит свое окно или пользователь введет опреде-

ленную информацию [12]. Ниже представлены основные функции, предназначенные для

работы с событиями:

 



 

 

HANDLE CreateEvent(

LPSECURITY_ATTRIBUTES lpsa,

 

BOOL bManualReset,

 

BOOL bInitialState,

 

LPTSTR lpszEventName);

 

BOOL SetEvent (HANDLE hEvent);

BOOL ResetEvent(HANDLE hEvent);


 

// привилегии доступа

// (по умолчанию = NULL)

// TRUE - событие должно быть

// сброшено вручную

// TRUE - создание события в

// сигнальном состоянии

//имя события (допускается

// значение NULL)


 

При установке параметра bInitialState функция CreateEvent создает событие, кото-

рое сразу же будет находиться в сигнальное состоянии. Функции SetEvent и ResetEvent в

случае успешного завершения возвращают значение TRUE, при возникновении ошибки

- значение FALSE.

Параметр bManualReset функции CreateEvent позволяет создать событие, сбрасы-

ваемое не вручную, а автоматически. Автоматически сбрасываемое событие переходит в

несигнальное состояние сразу же после выполнения функции SetEvent. Для таких собы-

тий функция ResetEvent является избыточной. Кроме того, перед автоматическим сбро-

сом по каждому сигналу событие освобождает только один поток. Автоматически сбра-

сываемые события целесообразно применять в таких программах, где один основной

поток подготавливает данные для других, вспомогательных потоков. При готовности

нового набора данных основной поток устанавливает событие, по которому освобожда-

ется один вспомогательный поток. Остальные вспомогательные потоки продолжают

ожидать подготовки новых данных.

Наряду с выполнением операций установки и сброса события можно сгенериро-

вать импульсное событие:

 

BOOL PulseEvent(hEvent);

 

Импульсное событие включает сигнал на короткий промежуток времени. Приме-

нение этой функции для события, сбрасываемого вручную, позволяет оповестить о нем

все ожидающие потоки, а затем сбросить событие. Вызов функции для события, сбрасы-

ваемого автоматически, дает возможность оповестить только один ожидающий поток.

Если не было ни одного ожидающего потока, то никакой другой поток не будет опове-

щен. С другой стороны, установка автоматического события позволит оставить сигнал

включенным до тех пор, пока не появится ожидающий его поток. После оповещения по-

тока событие сбрасывается автоматически.

Семафоры, исключающие семафоры и события могут совместно использоваться

несколькими процессами, которые необязательно должны быть связаны друг с другом.

Путем совместного задействования синхронизирующих объектов процессы могут коор-

динировать свои действия по аналогии с тем, как это делают потоки. Существует три

механизма совместного использования. Первый из них - это наследование, при котором

один процесс создает новый процесс, получающий копии всех дескрипторов родитель-

ского процесса. Копируются только те дескрипторы, которые при создании были поме-

чены как доступные для наследования.

Два других метода сводятся к созданию второго дескриптора существующего объ-

екта с помощью вызова функций. Какая из функций будет вызвана, зависит от имею-

щейся информации. При наличии дескрипторов как исходного процесса, так и процесса,

назначения следует вызывать функцию DuplicateHandle, при наличии только имени объ-

 

 



 

 

екта - одну из функций Openxxx. Две программы могут заранее определить имя совме-

стно используемого объекта. Кроме того, одна из программ способна передать другой

это имя посредством совместно используемой области памяти функций DDEML (DDE

Management Library - библиотека управления динамическим обменом данных) или кана-

ла.

 

BOOL DuplicateHandle(


HANDLE hSourceProcess,

 

HANDLE hSource,

HANDLE hTargetProcess,

 

LPHANDLE lphTarget,

DWORD fdwAccess,

BOOL bInherit,

DWORD fdwOptions);

 

HANDLE OpenMutex(

DWORD fdwAccess,

BOOL binherit,

 

LPTSTR lpszName);

 

HANDLE OpenSemaphore(

DWORD fdwAccess,

BOOL bInherit,

 

LPTSTR lpszName);

 

HANDLE OpenEvent(

DWORD fdwAccess,

BOOL bInherit,

 

LPTSTR lpszName);


// процесс, которому принадлежит

// исходный объект

// дескриптор исходного объекта

// процесс, который хочет создать

// копию дескриптора

// переменная для записи копии дескриптора

// запрашиваемые привилегии доступа

// может ли наследоваться копия дескриптора?

// дополнительные операции, например

// закрытие исходного дескриптора

 

//запрашиваемые привилегии доступа

// TRUE - дочерний процесс может

// наследовать этот дескриптор

// имя исключающего семафора

 

 

// запрашиваемые привилегии доступа

//TRUE - дочерний процесс может

// наследовать этот дескриптор

// имя семафора

 

 

// запрашиваемые привилегии доступа

// TRUE - дочерний процесс может

// наследовать этот дескриптор

// имя события


 

Используемый в этом примере тип данных LPTSTR - это обобщенный текстовый

тип, который компилируется по-разному в зависимости от того, какой стандарт, Unicode

или ASCII, поддерживается приложением.

Семафоры, исключающие семафоры и объекты событий будут сохраняться в памя-

ти до тех пор, пока не завершатся все использующие их процессы или пока с помощью

функции CloseHandle не будут закрыты все дескрипторы соответствующего объекта:

BOOLCloseHandle(hObject);

Критическийраздел представляет собой объект, выполняющий те же функции,

что и исключающий семафор, но в отличие от последнего критический раздел не может

наследоваться. Оба объекта доступны только для одного процесса. Преимущество кри-

тических разделов перед исключающими семафорами состоит в том, что они проще в

управлении и гораздо быстрее работают [12].

Терминология, принятая для функций, которые используются при работе с крити-

ческими разделами, отличается от терминологии, разработанной для функций управле-

ния семафорами, исключающими семафорами и событиями, однако сами функции вы-

полняют одни и те же операции. В частности, принято говорить не о создании

 

 



 

 

критического раздела, а о его инициализации. Процесс не ожидает критический раздел, а

входит в него, и не освобождает критический раздел, а покидает его; к тому же вы не

закрываете дескриптор, а удаляете объект.

 

VOID InitializeCriticalSection (LPCRITICAL_SECTION lpcs);

VOID EnterCriticalSection(LPCRITICAL_SECTION lpcs);

VOID LeaveCriticalSection(LPCRITICAL_SECTION lpcs);

VOID DeleteCriticalSection(LPCRlTICAL_SECTION lpcs);

 

Переменная типа LPCRITICAL_SECTION содержит указатель (а не дескриптор)

критического раздела. Функция InitializeCriticalSection должна получить указатель на

пустой объект (&cs), который можно создать следующим образом:

CRITICAL SECTION cs;

 

2.2.4. Использование классов MFC для создания потоков

 

Способ создания потоков с помощью функций библиотеки MFC, заключается в

создании класса, порожденного от класса CWinThread. Схема этого процесса выглядит

следующим образом:

 

// Класс CThreadExample

IMPLEMENT_DYNCREATE(CThreadExample, CWinThread)

 

CThreadExample::CThreadExample()

{


 

}



// инициализация переменных-членов класса


 

CThreadExample::~CThreadExample()

{

}

 

BOOL CThreadExample::InitInstance()

{

// TODO: здесь следует выполнить инициализацию потока

//здесь должны выполняться операции инициализации,

//не связанные с переменными, например создание

// экземпляров других объектов класса

return TRUE;

}

int CThreadExample::ExitInstance()

{

// TODO: здесь выполняются все операции очистки для потока

return CWinThread::ExitInstance();

}

 

BEGIN_MESSAGE_MAP(CThreadExample, CWinThread)

 

//{{AFX_MSG_MAP(CThreadExample)

 

 



 

 

// ПРИМЕЧАНИЕ - Мастер ClassWizard будет добавлять/удалять в

// этом месте макросы обработки сообщений

//}}AFX_MSG_MAP

END_MESSAGE_MAP ()

 

Объект класса CWinThread представляет поток выполнения в рамках приложения.

Хотя основной поток выполнения приложения обычно задается объектом класса, поро-

жденного от CWinApp, сам класс CWinApp является производным от класса

CWinThread.

Для обеспечения безопасности потоков в MFC-приложениях должны применяться

классы, являющиеся производными от класса CWinThread, библиотека MFC использует

переменные-члены этого класса. Потоки, созданные с помощью функции_beqinthreadex,

не могут использовать ни одной из API-функций библиотеки MFC.

Поддерживаются два основных типов потоков, а именно рабочие и интерфейс-

ные. Для рабочих потоков не нужно создавать цикл обработки сообщений. Такие потоки

могут выполнять фоновые вычисления в электронной таблице без взаимодействия с

пользователем и не должны реагировать на сообщения.

Интерфейсные потоки, в отличие от рабочих, обрабатывают сообщения, получен-

ные от системы (или от пользователя). Для них необходима специальная процедура об-

работки сообщений. Создаются интерфейсные потоки на базе класса CWinApp или не-

посредственно класса CWinThread.

Объект класса CWinThread обычно существует в течение всего времени существо-

вания потока, однако такой способ функционирования можно изменить, присвоив пере-

менной-члену m_bAutoDelete значение FALSE.

Потоки создаются с помощью функции AfxBeqinThread. Для создания интерфейс-

ного потока функции AfxBeqinThread следует передать указатель на класс CRuntimeC-

lass объекта, производного от класса CWinThread. В случае рабочих потоков функция

AfxBeqinThread вызывается с указанием управляющей функции и параметра, переда-

ваемого последней.

Как для рабочих, так для интерфейсных потоков можно указать дополнительные

параметры, изменяющие приоритет, размер стека, флаги создания и атрибуты безопас-

ности потока. Функция AfxBeqinThread возвращает указатель на новый объект класса

CWinThread.

В качестве альтернативного варианта можно определить, а затем создать объект,

производный от класса CWinThread, вызвав функцию CreateThread данного класса. В

этом случае производный объект может многократно использоваться при последова-

тельных созданиях и уничтожениях потока.

 

 


 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-04-02 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: