Здавалка
Главная | Обратная связь

Приостановка и возобновление потоков



В объекте ядра «поток» имеется переменная – счетчик числа простоев данного потока. При вызове CreateProcess или CreateThread он инициализируется значением, равным 1, которое запрещает системе выделять новому потоку процессорное время. Такая схема весьма разумна: сразу после создания поток не готов к выполнению, ему нужно время для инициализации.

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

Приостановив поток, можно настроить некоторые его свойства (например, изменить приоритет). Закончив настройку, необходимо разрешить выполнение потока вызовом функции ResumeThread:

DWORD ResumeThread(HANDLE hThread);

Если поток уже создан, приостановить его выполнение можно вызовом функции SuspendThread:

DWORD SuspendThread(HANDLE hThread);

Если выполняемый поток приостанавливается, происходит немедленное переключение к следующему потоку (рис. 10).

Рисунок 10 – Приостановка потока

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

void SuspendProcess(DWORD processID)

{

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

HANDLE threads = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,

processID);

 

if (threads != INVALID_HANDLE_VALUE)

{

THREADENTRY32 threadEntry = { sizeof(threadEntry) };

BOOL isOk = Thread32First(threads, &threadEntry);

 

for (; isOk; isOk = Thread32Next(threads, &threadEntry))

{

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

if (threadEntry.th32OwnerProcessID == processID)

{

HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME,

FALSE, threadEntry.th32ThreadID);

SuspendThread(hThread);

CloseHandle(hThread);

}

}

 

CloseHandle(threads);

}

}

Аналогичным образом можно реализовать функцию ResumeProcess, которая будет возобновлять работу «приостановленного» процесса. Однако пользуясь данным механизмом, следует учитывать, что после вызова функции CreateToolhelp32Snapshot состав потоков в данном процессе может измениться.

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

VOID Sleep(DWORD milliseconds);

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

Рисунок 11 иллюстрирует одну из возможных ситуаций, которая служит подтверждением сказанному. Поток 2 приостанавливает свое выполнение на 150 мс, однако в действительности получает процессорное время лишь через 200 мс, когда у потока 1 закончился квант.

Рисунок 11 – Поток может быть приостановлен на большее время, чем ожидалось

Приоритеты потоков

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

Потоку присваивается приоритет от 0 (самый низкий) до 31 (самый высокий). Таким образом, система в первую очередь будет выделять процессорное время потокам с самым высоким приоритетом (до тех пор, пока они не завершатся). В связи с этим, возможно, что потоки с более низким приоритетом будут простаивать. Однако подобные ситуации встречаются довольно редко, так как подавляющая часть потоков выполняется с одним приоритетом, к тому же, большинство из них находится в состоянии ожидания некоторого события (например, ввода с клавиатуры) и пока таковое не произошло, эти потоки не участвуют в распределении процессорного времени.

Потоки с более высоким приоритетом всегда вытесняют потоки с более низким приоритетом независимо оттого, исполняются последние или нет. Иначе говоря, при появлении потока с более высоким приоритетом, активный поток немедленно приостанавливается – даже если не истек отведенный ему квант процессорного времени – и происходит переключение на высокоприоритетный поток (рис. 12).

Рисунок 12 – Потоки с высоким приоритетом вытесняют потоки с низким приоритетом

Значение приоритета потока вычисляется самой системой, исходя из класса приоритета процесса и относительного приоритета потока. Класс приоритета процесса задает минимальное значение приоритета его потоков, а относительный приоритет потока – смещение от указанного минимума.

Windows поддерживает шесть классов приоритета (рис. 13):

- real time (реального времени);

- high (высокий);

- above normal (выше среднего);

- normal (средний);

- below normal (ниже среднего);

- idle (низкий).

Рисунок 13 – Классы приоритета процесса

Существует семь относительных приоритетов потоков:

- time-critical (критичный по времени).

- highest (высший);

- above normal (выше среднего);

- normal (средний);

- below normal (ниже среднего);

- lowest (низший);

- idle (простаивающий).

Класс приоритета процесса можно указать при его создании (п. 4.2.5), передав функции CreateProcess нужное значение:

STARTUPINFO startInfo = { sizeof(startInfo) };

PROCESS_INFORMATION procInfo;

 

// Создание дочернего процесса с классом приоритета "Выше среднего"

if (CreateProcess(..., ABOVE_NORMAL_PRIORITY_CLASS, ...) == TRUE)

{

CloseHandle(procInfo.hThread);

CloseHandle(procInfo.hProcess);

}

Функция SetPriorityClass позволяет изменить класс приоритета существующего процесса:

BOOL SetPriorityClass(HANDLE hProcess, DWORD priorityClass);

Все потоки создаются с относительным приоритетом normal. Функция SetThreadPriority позволяет изменить приоритет существующего потока:

BOOL SetThreadPriority(HANDLE hThread, int priority);

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

STARTUPINFO startInfo = { sizeof(startInfo) };

PROCESS_INFORMATION procInfo;

 

// Создание приостановленного процесса

if (CreateProcess(..., CREATE_SUSPENDED, ...) == TRUE)

{

CloseHandle(procInfo.hProcess);

// Установка относительного приоритета первичного потока

// "выше среднего"

SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_ABOVE_NORMAL);

// "Запуск" потока

ResumeThread(procInfo.hThread);

CloseHandle(procInfo.hThread);

}

Задания

Начиная с Windows 2000, появилось специализированное расширение модели процессов – объект ядра «задание» (job). Объект-задание позволяет управлять группой процессов, как единым целым, причем для такой группы можно указать ряд ограничений на определенные действия процессов. Процесс может входить только в одно задание. По умолчанию его связь с объектом «задание» нельзя разрушить, и все процессы, создаваемые данным процессом и его потомками, будут сопоставлены с тем же заданием [4]. Объект «задание» также регистрирует базовую учетную (статистическую) информацию всех включенных в него процессов, в том числе уже завершившихся.

Суть группировки процессов с помощью объекта ядра «задание» сводится к следующей последовательности действий:

- создание объекта-задания;

- связывание задания с ограничениями;

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

- добавление процессов в задание.

Создание задания осуществляется вызовом функции CreateJobObject:

HANDLE CreateJobObject(PSECURITY_ATTRIBUTES psa, PCTSTR name);

Как и любая функция, создающая объекты ядра, CreateJobObject принимает в первом параметре информацию о защите – ссылку на структуру PSECURITY_ATTRIBUTES, – где можно указать возможность наследования дескриптора созданного объекта (свойство bInheritHandle). Параметр name позволяет присвоить заданию имя, что бы к нему могли обращаться другие процессы через функцию OpenJobObject:

HANDLE OpenJobObject(DWORD desiredAccess, BOOL inheritHandle, PCTSTR name);

По завершению работы с заданием, необходимо закрыть его дескриптор вызовом функции CloseHandle. Закрытие объекта-задания не приводит к автоматическому завершению всех его процессов. Данный объект просто помечается как подлежащий разрушению, и система уничтожает его только после завершения всех включенных в него процессов.







©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.