Чтение онлайн

на главную - закладки

Жанры

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

Кёртен Роб

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

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

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

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

запускает целый букет потоков, а затем отображает результаты. Но как функция узнает, когда уже можно выводить результаты?

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

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

 ...

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

 while (num_lines_completed < num_x_lines) {

sleep(1);

 }

}

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

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

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

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

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

#include <pthread.h>

int pthread_join(pthread_t thread, void **value_ptr);

Функции pthread_join передается идентификатор потока, к которому вы желаете присоединиться, а также необязательный аргумент value_ptr, который может быть использован для сохранения возвращаемого присоединяемым потоком значения (Вы можете передать вместо этого параметра NULL, если это значение для вас не представляет интереса — в данном примере мы так и сделаем).

Где нам брать идентификатор потока? Мы игнорировали его в функции pthread_create, передав NULL в качестве первого параметра. Давайте исправим нашу программу:

int num_lines_per_cpu;

int num_cpus;

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

 int cpu;

 pthread_t *thread_ids;

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

 thread_ids = malloc(sizeof(pthread_t) * num_cpus);

 num_lines_per_cpu = num_x_lines / num_cpus;

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

pthread_create(

&thread_ids[cpu], NULL, do_one_batch, (void*)cpu);

 }

 // Синхронизироваться с завершением всех потоков

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

pthread_join(thread_ids[cpu], NULL);

 }

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

}

Обратите

внимание, что на этот раз мы передали функции pthread_create в качестве первого аргумента указатель на
pthread_t
. Там и будет сохранен идентификатор вновь созданного потока. После того как первый цикл
for
завершится, у нас будет num_cpu работающих потоков, плюс поток, выполняющий main. Потребление ресурсов процессора потоком main нас мало интересует — этот поток потратит все свое время на ожидание.

Ожидание достигается применением функции pthread_join к каждому из наших потоков. Сначала мы ждем завершения потока

thread_ids[0]
. Когда он завершится, функция pthread_join разблокируется. Следующая итерация цикла
for
заставит нас ждать завершения потока
thread_ids[1]
, и так далее для всех num_cpus потоков.

В этот момент возникает законный вопрос: «А что если потоки завершат работу в обратном порядке?» Другими словами, если имеются 4 процессора, и по какой-либо причине поток, выполняющийся на последнем процессоре (с номером 3), завершит работу первым, затем завершится поток, выполняющийся на процессоре с номером 2, и так далее? Вся прелесть приведенной схемы заключается в том, что ничего плохого не произойдет.

Первое, что произойдет — это то, что pthread_join блокируется на

thread_ids[0]
. Тем временем пусть завершится поток
thread_ids[3]
. Это не окажет абсолютно никакого воздействия на поток main, который будет по-прежнему ждать завершения первого потока. Затем, пусть завершит работу поток
thread_ids[2]
. По-прежнему, никаких последствий. И так далее — пока не завершит работу поток
thread_ids[0]
.

В этот момент pthread_join разблокируется, и мы немедленно переходим к следующей итерации цикла

for
. Вторая итерация цикла for применит pthread_join к потоку
thread_ids[1]
, который не будет блокирован, и итерация завершится немедленно. Почему? Потому что поток, идентифицированный как
thread_ids[1]
, уже завершился. Поэтому наш цикл for просто «проскочит» остальные потоки и завершится. В этот момент мы будем знать, что вычислительные потоки синхронизированы, и теперь мы можем выводить результаты отображение.

Применение барьера

Когда мы говорили о синхронизации функции main по моменту завершения рабочих потоков (в параграфе «Синхронизация по отношению к моменту завершения потока», см. выше), мы упомянули два метода синхронизации: один метод с применением функции pthread_join, который мы только что рассмотрели, и метод с применением барьера.

Возвращаясь к нашей аналогии с процессами в жилом доме, предположим, что семья пожелала где-нибудь отдохнуть на природе. Водитель садится в микроавтобус и запускает двигатель. И ждет. Водитель будет ждать до тех пор, пока все члены семьи не сядут в машину, и только затем можно будет ехать — не можем же мы кого-нибудь оставить!

Поделиться:
Популярные книги

Идеальный мир для Лекаря 10

Сапфир Олег
10. Лекарь
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 10

Бестужев. Служба Государевой Безопасности. Книга четвертая

Измайлов Сергей
4. Граф Бестужев
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга четвертая

Светлая тьма. Советник

Шмаков Алексей Семенович
6. Светлая Тьма
Фантастика:
юмористическое фэнтези
городское фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Светлая тьма. Советник

Лисья нора

Сакавич Нора
1. Всё ради игры
Фантастика:
боевая фантастика
8.80
рейтинг книги
Лисья нора

Калибр Личности 5

Голд Джон
5. Калибр Личности
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Калибр Личности 5

(Не)нужная жена дракона

Углицкая Алина
5. Хроники Драконьей империи
Любовные романы:
любовно-фантастические романы
6.89
рейтинг книги
(Не)нужная жена дракона

Сердце Дракона. Том 10

Клеванский Кирилл Сергеевич
10. Сердце дракона
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
7.14
рейтинг книги
Сердце Дракона. Том 10

Белые погоны

Лисина Александра
3. Гибрид
Фантастика:
фэнтези
попаданцы
технофэнтези
аниме
5.00
рейтинг книги
Белые погоны

Как я строил магическую империю

Зубов Константин
1. Как я строил магическую империю
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Как я строил магическую империю

Повелитель механического легиона. Том VII

Лисицин Евгений
7. Повелитель механического легиона
Фантастика:
технофэнтези
аниме
фэнтези
5.00
рейтинг книги
Повелитель механического легиона. Том VII

Я князь. Книга XVIII

Дрейк Сириус
18. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я князь. Книга XVIII

На границе империй. Том 10. Часть 2

INDIGO
Вселенная EVE Online
Фантастика:
космическая фантастика
5.00
рейтинг книги
На границе империй. Том 10. Часть 2

Вечный. Книга I

Рокотов Алексей
1. Вечный
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Вечный. Книга I

Отморозок 3

Поповский Андрей Владимирович
3. Отморозок
Фантастика:
попаданцы
5.00
рейтинг книги
Отморозок 3