Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
375
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

SuspendThread (или ResumeThread) OpenThread имеется только в Windows 2000, поэтому моя функция SuspendProcess не будет работать ни в Windows 95/98, ни в Windows NT 4 0

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

зобновит поток, который собственно и не приостанавливался. Но может быть еще хужепри перечислении текущий поток уничтожается и создастся новый с тем же идентификатором. Тогда моя функция приостановит неизвестно какой поток (и даже непонятно в каком процессе).

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

Функция Sleep

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

VOID Sleep(DWORD dwMilliseconds);

Эта функция приостанавливает поток па dwMilliseconds миллисекунд. Отметим несколько важных моментов, связанных с функцией Sleep.

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

равный заданному, Все верно: если Вы укажете остановить поток на 100 мс, приблизительно на столько он и "заснет", хотя не исключено, что его сон про длится на несколько секунд или даже минут болыше Вспомните, Windows не является системой реального времени. Ваш поток может возобновиться в за данный момент, но это зависит от того, какая ситуация сложится в системе к тому времени.

Вы можете вызвать Slecp и передать в dwMilliseconds значение INFINITE, вооб ще запретив планировать поток. Но это не очень практично — куда лучше корректно завершить поток, освободив сго стек и объект ядра.

Вы можете вызвать Sleep и передать в dwMilliseconds нулевое значение. Тогда Вы откажетесь от остатка своего кванта времени и заставите систему подклю чить к процессору другой поток. Однако система может снова запустить Ваш поток, если других планируемых потоков с тем же приоритетом нет.

Переключение потоков

Функция SwitchToThread позволяет подключить к процессору другой поток (если он есть):

BOOL SwitchToThread();

Когда Вы вызываете эту функцию, система проверяет, есть ли поток, которому не хватает процессорного времени Если нет, SwitchToThread немедленно возвращает управление, а если да, планировщик отдает ему дополнительный квант времени (при оритет этого потока может быть ниже, чем у вызывающего). По истечении , этого кван та планировщик возвращается в обычный режим работы

SwitchToThread позволяет потоку, которому не хватает процессорного времени, отнять этот ресурс у потока с более низким приоритетом. Она возвращает FALSE, если на момент ее вызова в системе нет ни одного потока, готового к исполнению, в ином случае

ненулевое значение.

Вызов SwitchToThread аналогичен вызову Sleep с передачей в dwMilliseconds нуле вого значения. Разница лишь в том, что SwitchToThread дает возможность выполнять потоки с более низким приоритетом, которым не хвачает процессорного времени, а Sleep действует без оглядки на "голодающие" потоки.

WIDOWS 98

В Windows 98 функция SwitchToThread лишь определена, но не реализована

Определение периодов выполнения потока

Иногда нужно знать, сколько времени затрачивает поток на выполнениетой или иной операции Многие в таких случаях пишут что-то вроде этого:

// получаем стартовое время

DWORD dwStartTime = GetTickCount();

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

//вычитаем стартовое время из текущего

DWORD dwElapsedTime = GetTickCount() - dwSlartTime;

Этот код основан на простом допущении, что он нс будет прерван. Но в операци онной системе с вытесняющей многозадачностью никто не знает, когда поток полу чит процессорное время, и результат будет сильно искажен. Что нам здесь нужно, так это функция, которая сообщает время, затраченное процессором на обработку дан ного потока. К счастью, в Windows есть такая функция:

BOOL GetThreadTimes( HANDLE hThread, PFILETIME pftCreationTime, PFILETIMt pftExitTime, PFILETIME pftKernelTime, PFIIFTIME pftUserTime);

GetThreadTimes возвращает четыре временных параметра:

Показатель времени

Описание

 

 

Время coздания (creation time)

Абсолютная величина, выраженная в интервалах по 100 нс.

 

Отсчитывается с полуночи 1 января 1601 года по Гринвичу до

 

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

 

 

Время завершении (exit time)

Абсолютная величина, выраженная в интервалах по 100 нс

 

Отсчитывается с полуночи 1 января 1601 года по Гринвичу до

 

момента завершения потока. Если поток все еще выполняется, этот

 

показатель имеет неопределенное значение

 

 

Время выполнения ядра (kernel Относительная величина, выраженная в интерва лах по 100 нс. time) Сообщает время, затраченное этим потоком на выполнение кода

операцион ной системы

Бремя выполнения User (User time) Относительная величина, выраженная в интерва лах по 100 не Сообщает время, затраченное по током на выполнение кода приложения.

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

_int64 FileTimeToQuadWord(PFILETIME ptt)

{

return(Int64ShllMod32(pft->dwHighDateTime, 32) | pft- >dwLowDateTime);

}

void PerformLongOperation ()

{

FILETIME ftKernelTimeStart, ftKernelTimeEnd;

FILETIME ftUserTimeStart, ftUserTirreEnd;

FILETIME ftDummy;

_int64 qwKernelTimeElapsed, qwUserTimeElapsed, qwTotalTimeElapsed;

//получаем начальные показатели времени

GetThreadTimes(GetCurrentThrcad(), &ftDurrmy, &ftDummy, &ftKernelTirrieStart, &ttUserTimeStart);

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

//получаем конечные показатели времени

GetThreadTimes(GetCurrentThread(), &ftDumrny, &ftDummy, &ftKernelTimeEnd, &ftUserTimeEnd);

//получаем значении времени, затраченного на выполнение ядра и

User,

//преобразуя начальные и конечные показатели времени из FILETIME

//в учетверенные слова, а затем вычитая начальные показатели из конечных

qwKernelTimeElapsed = FileTimeToQuadWord(&ftKernelTimeEnd) - FileTimeToQuadWord(&ftKernelTimeStart);

qwUserTimeElapsed = FileTimeToQuadWord(&ftUserTimeFnd) - FileTimeToQuadWord(&riUserTimeStart);

//получаем общее время, складывая время выполнения ядра и User qwTotalTimeElapsed = qwKernelTimeElapsed + qwUserTimeElapsed;

//общее время хранится в qwTotalTimeElapsed

}

Заметим, что существует еще одна функция, аналогичная GetThreadTimes и при менимая ко всем потокам в процессе:

BOOL GetPrucessTimes( HANDLE hProcess, PFILETIHE pftCreationTime, PFILETIME pftExitTime, PFILETIME pftKernelTime, PFILETIME pftUserTime);

GetProcessTimes возвращает временные параметры, суммированные по всем пото кам (даже уже завершенным) в указанном процессе Так, время выполнения ядра бу дет суммой периодов времени, затраченного всеми потоками процесса на выполне ние кода операционной системы.

WINDOWS 98

К сожалению, в Windows 98 функции GetThreadTimes и GetProcessTimes опре делены, но не реализованы, Так что в Windows 98 нет надежного механизма, с помощью которого можно было бы определить, сколько процессорного вре мени выделяется потоку или процессу.

GetThreadTimes не годится для высокоточного измерения временных интервалов — для этого в Windows предусмотрено двe специальные функции:

BOOL QueryPerformanceFrequency(LARGE_INTEGER* pliFrequency); BOOL QueryPerformanceCounler(LARGE_INTEGER* pliCount);

Они построены на том допущении, что поток не вытесняется, поскольку высоко точные измерения проводятся, как правило, в очень быстро выполняемых блоках кода. Чтобы слегка упростить работу с этими функциями, я создал следующий С++ - класс:

class CStopwatch

{

public:

CStopwatch() { QueryPerformanceFrequency(&m_liPeifFreq), Start();

}

void Start() { QueryPerformanceCounter(&m_liPerfStart); }

_irt64 Now() const

{ // возвращает число миллисекунд после вызова Start

LARGE_INTEGER liPerfNow; QueryPerformanceCounter(&liPerfNow);

return(((liPerfNow.QuadPart - m_liPerfStart.QuadPart) * 1000) / m_liPerfFreq.QuadPart);

}

private

LARGE_INTEGER m_liPerfFreq;

// количество отсчетов в секунду

LARGE_INTEGER m_liPerfStart; // начальный отсчет

};

Яприменяю этот класс так:

//создаю секундомер (начинающий отсчет с текущего момента времени)

CStopwatch stopwatch;

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

//определяю, сколько времени прошло

__int64 qwElapsedTime = stopwatch Now();