Прогон программы, демонстрация приоритетного планирования

Динамическое повышение приоритета

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

Например, после завершения операции ввода-вывода увеличивают приоритет потока, чтобы дать ему возможность быстрее начать выполнение и, может быть, вновь инициировать операцию ввода-вывода. Таким способом система поощряет интерактивные потоки и поддерживает занятость устройств ввода-вывода. Величина, на которую повышается приоритет, не документирована и зависит от устройства (рекомендованные значения для диска и CD – это 1, для сети – 2, клавиатуры и мыши – 6 и звуковой карты – 8). В дальнейшем в течение нескольких квантов времени приоритет плавно снижается до базового.

Другими примерами подобных ситуаций могут служить: пробуждение потока после состояния ожидания семафора или иного события; получение потоком доступа к оконному вводу.

Динамическое повышение приоритета решает также проблему голодания потоков, долго не получающих доступ к процессору. Обнаружив такие потоки, простаивающие в течение примерно 4 сек., система временно повышает их приоритет до 15 и дает им два кванта времени. Побочным следствием применения этой технологии может быть решение известной проблемы инверсии приоритетов. Эта проблема возникает, когда низкоприоритетный поток удерживает ресурс, блокируя высокоприоритетные потоки, претендующие на этот ресурс. Решение состоит в искусственном повышении его приоритета на некоторое время.

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

#include <windows.h>

#include <stdio.h>

#include <math.h>

 

 

void Calculations()

{

int I,N=50000000;

double a,b;

for ( I = 0; i<N; i++) {

b=(double)I / (double)N;

a=sin(b);

}

}

 

DWORD WINAPI SecondThread( LPVOID lpParam )

{

 

printf(“Begin of Second Thread\n”);

Calculations();

printf(“End of Second Thread\n”);

 

return 0;

}

 

VOID main( VOID )

{

DWORD dwThreadId, dwThrdParam;

HANDLE hThread;

 

hThread = CreateThread(

NULL,

0,

SecondThread,

&dwThrdParam,

0,

&dwThreadId);

 

if (hThread == NULL)

{

printf(“CreateThread failed\n” );

return;

}

 

SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);

 

SuspendThread(hThread);

getchar();

ResumeThread(hThread);

 

printf(“Begin of First Thread\n”);

Calculations();

printf(“End of First Thread\n”);

}

В приведенной программе два параллельных потока выполняют длительный счетный цикл (подпрограмма Calculations). Второй поток в силу более высокого приоритета выполняется раньше. Пара функций Suspend/ResumeThread (приостановка и возобновление потока) используется для фиксации начала соревнования. Если закомментировать SetThreadPriority, то можно будет увидеть, что оба потока заканчивают работу одновременно.

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