struct timespec __ss_repl_period;
struct timespec __ss_init_budget;
} __ss;
где low_priority
— фоновый приоритет; max_repl
— максимальное количество пополнений бюджета за период; repl_period
— период пополнения бюджета и init_budget
— начальный бюджет.
Соображения производительности
Выполним «симметричный» тест аналогично тому, как это делалось для переключения контекстов процессов (стр. 44), но теперь применительно к потокам ( файл p5t.cc ). При этом мы постараемся максимально сохранить принципы функционирования, имевшие место в приложении «Затраты на взаимное переключение процессов» ( файл p5.сс ) (естественно, из-за принципиального различия механизмов тексты кодов будут существенно отличаться).
Затраты на взаимное переключение потоков
#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
# nice -n-19 p5t -n1000
2 : cycles - 753269; on sched - 376
3 : cycles - 752069; on sched - 376
# nice -n-19 p5t -n10000
2 : cycles - 7494255; on sched - 374
3 : cycles - 7493225; on sched - 374
# nice -n-19 p5t -n100000
2 : cycles - 74897795; on sched - 374
3 : cycles - 74895800; on sched — 374
# nice -n-19 p5t -n1000000
2 : cycles - 748850811, on sched - 374
3 : cycles - 748850432; on sched - 374
Как и в случае с процессами, результаты отличаются очень высокой устойчивостью при изменении «объема вычислений» на 4 порядка, однако по своим величинам значения для потоков почти в 2 раза меньше, чем для процессов (стр. 45).
Как и в случае обсуждавшегося ранее завершения процесса, для потоков мы будем отчетливо различать случаи:
• «естественного» завершения выполнения потока из кода самого потока;
• завершения потока извне, из кода другого потока или по сигналу. Для этого действия, в отличие от «естественного» завершения, будем использовать другой термин — отмена.
Завершение потока происходит при достижении функцией потока своего естественного конца и выполнения оператора return
(явно или неявно) или выполнения потоком вызова:
void pthread_exit(void* value_ptr)
где value_ptr
— указатель на результат выполнения потока.
При выполнении pthread_exit()
поток завершается. Если этот поток принадлежит к категории ожидаемых, он может возвратить результат своей работы другому потоку, ожидающему его завершения на вызове pthread_join()
(только один поток может получить результат завершения). Если же этот поток отсоединенный, то по его завершении все системные ресурсы, задействованные потоком, освобождаются немедленно.
Перед завершением потока будут выполнены все завершающие процедуры, помещенные в стек завершения, а также деструкторы собственных данных потока, о которых мы говорили ранее. Для последнего потока процесса вызов pthread_exit()
эквивалентен exit()
.
Возврат результата потока
Выше отмечено, что вызов pthread_exit()
, завершающий ожидаемый поток, может передать результат выполнения потока. То же действие может быть выполнено и оператором return
потоковой функции, которая из прототипа ее определения должна возвращать значение типа void*
.
В обоих случаях результат может иметь сколь угодно сложный структурированный тип; никакая типизация результата не предусматривается (тип void*
). Важно, чтобы код, ожидающий результата на вызове pthread_join()
, понимал его так же, как и функция потока, возвращающая этот результат.
Читать дальше
Конец ознакомительного отрывка
Купить книгу