На рис. 2.6 проиллюстрирована работа спорадического потока. После запуска (момент времени 0) поток переходит в блокированное состояние на время R (10 мс), но его бюджет все равно расходуется. Поток становится активным, но через 3 мс (13 мс от начала выполнения) вытесняется более приоритетным потоком. Факт вытеснения означает, что через период пополнения T (40 мс) бюджет потока будет пополнен на израсходованную величину (13 мс). Еще через 3 мс более приоритетный поток заканчивает свою работу и управление возвращается назад. От начального бюджета потока С (20 мс) осталось еще 7 мс, и поток выполняется это время с основным приоритетом. При этом от повторного начала его выполнения (16 мс) отсчитывается новый период пополнения, то есть через 56 мс бюджет потока будет пополнен на 7 мс. После полного исчерпания бюджета приоритет потока понижается до фонового (L) и поток может вытесняться или нет в зависимости от приоритетов остальных потоков в системе. После наступления очередного времени пополнения бюджет потока восстанавливается на израсходованную в этом периоде величину и т.д.
Рис. 2.6. Периодическое выполнение спорадической задачи
Если поток много раз вытесняется в период своей работы с основным приоритетом, то его выполнение может превратиться в многократное колебание с высокой частотой между основным и фоновым приоритетами. Поэтому в QNX 6.2.1 в параметрах для спорадической диспетчеризации можно установить (ограничить) максимальное количество пополнений бюджета за период.
Как уже описывалось выше, структура
shed_param
содержит в своем составе, в частности, еще и структуру параметров для спорадической диспетчеризации (при других типах диспетчеризации эта часть не используется):
struct {
_INT32 __ss_low_priority;
_INT32 __ss_max_repl;
struct timespec __ss_repl_period;
struct timespec __ss_init_budget;
} __ss;
где
low_priority
— фоновый приоритет;
max_repl
— максимальное количество пополнений бюджета за период;
repl_period
— период пополнения бюджета и
init_budget
— начальный бюджет.
Соображения производительности
Выполним «симметричный» тест аналогично тому, как это делалось для переключения контекстов процессов (стр. 44), но теперь применительно к потокам (
#include
#include
#include
#include
#include
#include
#include
#include
unsigned long N = 1000;
// потоковая функция:
void* threadfunc(void* data) {
uint64_t t = ClockCycles;
for (unsigned long i = 0; i < N; i++) sched_yield;
t = ClockCycles - t;
// дать спокойно завершиться 2-му потоку до начала вывода
delay(100);
cout << pthread_self << "\t: cycles - " << t
<< ", on sched - " << (t / N) / 2 << endl;
return NULL;
}
int main(int argc, char* argv[]) {
int opt, val;
while ((opt = getopt(argc, argv, "n:")) != -1) {
switch(opt) {
case 'n': // переопределения числа переключений
if (sscanf(optarg, "%i", &val) != 1)
cout << "parse command line error" << endl, exit(EXIT_FAILURE);
if (val > 0) N = val;
break;
default:
exit(EXIT_FAILURE);
}
}
const int T = 2;
pthread_t tid[T];
// создать взаимодействующие потоки
for (int i = 0; i < T; i++)
if (pthread_create(tid + i, NULL, threadfunc, NULL) != EOK)
cout << "thread create error", exit(EXIT_FAILURE);
// и дожидаться их завершения ...
for (int i = 0; i < T; i++)
pthread_join(tid[i], NULL);
exit(EXIT_SUCCESS);
}
Результаты выполнения программы:
# nice -n-19 p5t -n100
2 : cycles - 79490; on sched - 397
3 : cycles - 78350; on sched — 391