Шаг 282.
Модели потоков (общие сведения)

    На этом шаге мы приведем общие сведения о потоках.

    Для лучшего понимания вопросов вычислений в потоках необходимо совершить исторический экскурс и проанализировать развитие способов вычисления. Вследствие несовершенства технологии сборки первые созданные компьютеры имели значительную стоимость, поэтому сразу же встала задача увеличения их загрузки, что достигалось обработкой заданий в пакетном режиме - после завершения какой-либо задачи (или прерывания ее по времени) взамен загружалась другая задача, и расчеты продолжались уже для нее. По мере развития технологий сборки и удешевления компьютерного времени такой способ оказался неэффективным: если задача завершалась с кодом ошибки, то программист узнавал об этом не сразу, причем после внесения изменений и повторной постановки задачи в очередь не было никаких гарантий отсутствия других ошибок. Таким образом, для отладки приложений требовалось значительное время. Чтобы изменить эту ситуацию, были созданы операционные системы (UNIX), которые могли работать сразу с несколькими задачами. Все эти задачи загружались в память компьютера, и процессор начинал расчет для первой задачи. Затем, спустя некоторое время, запоминались состояния регистров процессора и их значения заменялись данными для следующей задачи. Опять же, спустя некоторое время, в регистрах процессора восстанавливались данные для первой задачи, и расчет продолжался для нее. Если такое переключение происходило достаточно часто, то у пользователя создавалась иллюзия, что компьютер одновременно решает несколько задач. Такой механизм обработки задач называется вытесняющей многозадачностью (preemptive multitasking). Его реализация позволила использовать терминалы, напрямую подключенные к компьютеру, и, таким образом, существенно повысить эффективность труда программистов.

    С появлением дешевых персональных компьютеров, казалось бы, надобность в многозадачности отпала, во всяком случае, операционная система DOS могла работать только в пакетном режиме. Однако оказалось, что это не так: пользователь часто хотел редактировать данные и при этом проводить какие-либо расчеты в фоновом режиме. Поэтому операционная оболочка Windows 3.1 уже была создана как многозадачная: пользователь мог запустить одновременно несколько приложений. Однако операционная оболочка Windows 3.1 не вытесняла задачи: переход к расчету для следующей задачи мог осуществляться только после завершения расчетов для предыдущей, когда очередь сообщений была свободна. Такой механизм расчетов называется невытесняющей многозадачностью (nonpreemptive multitasking). Если какая-либо задача входила в бесконечный цикл (или просто требовала значительного времени для своего решения), то пользователь не мог переключиться на другую задачу. Такая ситуация называется захватом процессора. Вытесняющая многозадачность была реализована в Windows, начиная с 32-разрядных версий (95/98/NT/2000/XP).

    Если операционная система поддерживает вытесняющую многозадачность, то почему бы не сделать следующий шаг, а именно: в рамках одной задачи заставить параллельно выполняться несколько различных участков исполняемого кода? Такой режим работы называют многопоточным (multithreading) и в этом случае говорят, что каждый участок кода выполняется в отдельном потоке. К сожалению, в русском языке термин "поток" используется также для обозначения потоков данных, поэтому там, где это необходимо, мы будем уточнять, о каком именно потоке идет речь: потоке выполнения (thread) или потоке данных (stream). Далее речь пойдет о потоках выполнения.

    Хотя программист может создать сколько угодно нотоков, на компьютерах с одним процессором не рекомендуется создавать более 16 потоков. Переключение между потоками занимает заметное время. Если, например, суммировать числа в массиве из 16 000 000 элементов в одной задаче и суммировать числа в 16 потоках в массивах из 1 000 000 элементов, то время выполнения второй задачи будет заметно больше. Однако при наличии нескольких процессоров разные потоки выполняются на разных процессорах, и время решения задачи существенно сокращается.

    На следующем шаге мы рассмотрим класс TThread.




Предыдущий шаг Содержание Следующий шаг