Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform | страница 39



>int num_lines_per_cpu;

>int num_cpus;


>int main (int argc, char **argv) {

> int cpu;


> ... // Выполнить инициализации


> // Получить число процессоров

> num_cpus = _syspage_ptr->num_cpu;

> num_lines_per_cpu = num_x_lines / num_cpus;

> for (cpu = 0; cpu < num_cpus; cpu++) {

>  pthread_create(NULL, NULL, do_one_batch, (void*)cpu);

> }



> ... // Вывести результат


>}


>void* do_one_batch(void *c) {

> int cpu = (int)c;

> int x1;

> for (x1 = 0; x1 < num_lines_per_cpu; x1++) {

>  do_line_line(x1 + cpu * num_lines_per_cpu);

> }

>}

Здесь мы запускаем только num_cpus потоков. Каждый поток будет выполняться на отдельном процессоре. А поскольку мы имеем дело с небольшим числом потоков, мы тем самым не засоряем память ненужными стеками. Обратите внимание, что мы получили число процессоров путем разыменования глобальной переменной — указателя на системную страницу _syspage_ptr. (Дополнительную информацию относительно системной страницы можно найти в книге «Building Embedded Systems» (поставляется в комплекте документации по QNX/ Neutrino — прим. ред.) или в заголовочном файле >).

Программирование для одного или нескольких процессоров

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

Синхронизация по отношению к моменту завершения потока

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

Заставлять main() заниматься опросом, закончены ли вычисления, противоречит самому замыслу ОС реального времени.

>int main (int argc, char **argv) {

> ...

> // Запустить потоки, как раньше

> while (num_lines_completed < num_x_lines) {

>  sleep(1);

> }

>}

He вздумайте писать такие программы!

Для решения этой задачи существуют два изящных решения: применение функций pthread_join() и barrier_wait().

«Присоединение» (joining)

Самый простой метод синхронизации — это «присоединение» потоков. Реально это действие означает ожидание завершения.

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