Вопросы реализации нитей
Существует два подхода к управлению нитями: статический и динамический. При статическом подходе вопрос, сколько будет нитей, решается уже на стадии написания программы или на стадии компиляции. Каждой нити назначается фиксированный стек. Этот подход простой, но негибкий. Более общим является динамический подход, который позволяет создавать и удалять нити оперативно по ходу выполнения. Системный вызов для создания нити обычно содержится в нити главной программы в виде указателя на процедуру с указанием размера стека, а также других параметров, например, диспетчерского приоритета. Вызов обычно возвращает идентификатор нити, который можно использовать в последующих вызовах, связанных с этой нитью. В этой модели процесс начинается с одной нити, но может создавать их еще, когда необходимо.
Завершаться нити могут одним из двух способов: по своей инициативе, когда завершается работа, и извне. Во многих случаях, например, при конвейерной модели, нити создаются сразу же после старта процесса и никогда не уничтожаются.
Поскольку нити разделяют общую память, они могут (и, как правило, делают это) использовать ее для сохранения данных, которые совместно используются множеством нитей, таких, например, как буфер в системе "производитель-потребитель". Доступ к разделяемым данным обычно программируется с использованием критических секций, предотвращающих попытки сразу нескольких нитей обратиться к одним и тем же данным в одно и то же время. Критическая секция наиболее легко реализуется с использованием семафоров, мониторов и аналогичных конструкций.
Нити могут быть реализованы как в пользовательском пространстве, так и в пространстве ядра. В первом случае нити работают на базе прикладной системы, управляющей всеми операциями с нитями. Первым преимуществом такого способа является то, что можно реализовать нити в операционной системе, которая их не поддерживает. ОС прикладная среда, управляющая нитями, кажется одним процессом. Все вызовы (ПРИОСТАНОВИТЬ, ПРОВЕРИТЬ СЕМАФОР и т. д.) обрабатываются как вызовы функций этой прикладной среды. Она сохраняет регистры и переключает указатели счетчика команд и стека. В этом случае переключение происходит быстрее, чем с помощью ядра. Такая реализация имеет еще одно преимущество - для каждого процесса можно организовать свою схему планирования. Однако этот подход связан с некоторыми проблемами, одна из которых состоит в следующем. При выполнении блокирующих системных вызовов приостанавливается весь набор нитей, принадлежащих этому процессу. Чтобы избежать этого, можно сделать все системные вызовы неблокирующими, но это требует изменений в ОС, что нежелательно, так как одной из целей реализации нитей в пользовательском пространстве является их работа в существующих операционных системах.
Такой проблемы не существует при реализации нитей в пространстве ядра. Преимущество заключается также и в том, что ядро может при диспетчеризации выбирать нить из другого процесса. Однако хотя механизм управления нитями аналогичен первому случаю, временные затраты на переключение нитей выше, так как тратится время на переключение из режима пользователя в режим ядра.