Вход/Регистрация
Основы программирования в Linux
вернуться

Мэтью Нейл

Шрифт:

У потоков есть и недостатки.

 Создание многопоточной программы требует очень тщательной разработки. Вероятность появления незначительных временных сбоев или ошибок, вызванных нечаянным совместным использованием переменных, в такой программе весьма значительна. Алан Кокс (Alan Сох, всеми уважаемый гуру Linux) сказал, что потоки равнозначны умению "выстрелить в обе собственные ноги одновременно".

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

 Программа, в которой громоздкие вычисления разделены на две части, и эти две части выполняются как отдельные потоки, необязательно будет работать быстрее на машине с одним процессором, если только вычисление не позволяет выполнять обе ее части одновременно и у машины, на которой выполняется программа, нет многоядерного процессора для поддержки истинной многопоточности.

Первая программа с применением потоков

Существует целый ряд библиотечных вызовов, связанных с потоками, большинство имен которых начинается с префикса pthread. Для применения этих библиотечных вызовов вы должны определить макрос

_REENTRANT
, включить файл pthread.h и скомпоновать программу с библиотекой потоков, используя опцию
– lpthread
.

Когда разрабатывались первые версии библиотечных подпрограмм UNIX и POSIX, предполагалось, что в каждом процессе будет только один поток исполнения. Яркий пример — переменная

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

Вам нужны реентерабельные подпрограммы. Реентерабельный программный код может вызываться несколько раз либо разными потоками, либо каким-то образом вложенными вызовами и при этом работать корректно. Следовательно, реентерабельная часть программного кода обычно должна применять локальные переменные таким образом, чтобы любой и каждый вызов кода получал собственную уникальную копию данных.

В многопоточных программах вы сообщаете компилятору, что вам нужно это средство, определяя в вашей программе макрос

_REENTRANT
до любых директив
#include
. При этом делаются три вещи и столь искусно, что обычно вам даже не нужно знать, какая работа проделана.

 Некоторые функции получают безопасный реентерабельный вариант прототипа или объявления. При этом имя функции остается обычно прежним, но в конце добавляется суффикс

_r
, например функция
gethostbyname
заменяется функцией
gethostbyname_r
.

 Некоторые функции из файла stdio.h, которые обычно реализованы как макросы, становятся соответствующими реентерабельными безопасными функциями.

 Переменная

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

Включение файла pthread.h предоставляет другие прототипы и определения, которые нужны в вашем программном коде, во многом так же, как делает stdio.h для подпрограмм стандартного ввода и вывода. В заключение следует убедиться в том, что вы включили в программу соответствующий заголовочный файл потоков и скомпоновали программу с подходящей библиотекой потоков, в которой реализованы функции семейства

pthread
. Позже в упражнении данного раздела приведены подробности, касающиеся компиляции вашей программы, но сначала рассмотрим новые функции, необходимые для управления потоками. Функция
pthread_create
создает новый поток во многом так же, как функция
fork
создает новый процесс.

#include <pthread.h>

int pthread_create(pthread_t * thread, pthread_attr_t *attr,

 void *(*start_routine)(void *), void *arg);

Прототип выглядит внушительно, но функцию очень легко применять. Первый аргумент — указатель на переменную типа

pthread_t
. Когда поток создан, в область памяти, на которую указывает эта переменная, записывается идентификатор. Этот идентификатор позволяет ссылаться на поток. Следующий аргумент задает атрибуты потока. Обычно нет нужды в особых атрибутах, и вы можете просто передать в этом аргументе
NULL
. Позже в этой главе вы увидите, как применять атрибуты потока. В последних двух аргументах потоку передается функция, которую он должен начать выполнять, и аргументы, которые нужно передать этой функции.

void *(*start_routine)(void *)

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

void
как параметр, и функция вернет указатель на
void
. Следовательно, вы можете передать единственный аргумент любого типа и вернуть указатель на любой тип. Применение функции
fork
заставит продолжить выполнение в том же месте, но с другим кодом возврата, в то время как использование нового потока непосредственно предоставит указатель на функцию, которую новый поток должен начать выполнять.

  • Читать дальше
  • 1
  • ...
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

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