Введение в 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().
Самый простой метод синхронизации — это «присоединение» потоков. Реально это действие означает ожидание завершения.
Присоединение выполняется одним потоком, ждущим завершения другого потока. Ждущий поток вызывает