Потоки исполнения как инструмент опытного "повара"

Ещё раз прочитайте внимательно вопрос в заголовке. Прочитали? А теперь без всякого промедления правильный ответ:

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

Мне на глаза как-то попался пример с приготовлением супа: обычно человек режет лук, потом режет помидоры, потом варит рис (будем считать, что это гипотетический суп, и вы бы такое есть не стали)… А мог бы сделать так: сначала бросить рис в кастрюлу и оставить его вариться, а потом, не дожидаясь окончания варки риса, начать резать лук и помидоры. Обычный человек так и поступит, если во время риса не отвлечётся на… другой поток исполнения… т.е. на другую «работу»: например, позвонит подруге или напишет на форум. В любом случае, за то время, в течение которого варился рис, можно дорезать все остальные ингредиенты салата, а после того, как рис всё-таки сварится — объединить всё в одной миске. При этом процедура нарезки овощей может выглядеть тоже не совсмем последовательно: например, нарезка лука может приводить к «слезам (счастья) из глаз», из-за чего периодически у вас будет возникать нестерпимое желание отвлечься на резку помидор. В то же самое время у вас в кастрюльке булькает рис — и если нет чётких временных критериев его готовности, нужно бы периодически проверять, а не разварился ли он ещё? Такая технология «варки супа» аналогична технологии выполнения потоков программного кода для достижения конечного (или промежуточного) результата — того или иного преобразования данных с выдачей результата этого преобразования — конечному пользователю или в качестве промежуточного результата — другому, более «крупному» заданию, частю которого являлась ваша работа.

Теперь предположим, что у вас есть помощник, и этот человек, например, будет варить рис. Вы же независимо от него приступите к нарезке овощей. В результате ваш помощник позаботится о том, чтобы рис был готов к тому моменту, когда мы закончим свою часть работы. Очень вероятно, что такой подход (метод) практически не сэкономит вам время: если продолжительность переключения вашего мозга от нарезки помидоров илои лука к проблеме варки риса пренебрежительно мала (вы хорошо выспались, быстро соображаете), а подготовительные операции «взять кастрюлю, налить воды, взять упаковку риса, засыпать рис в воду, поставить какстрюлю с рисом и водой на плиту, включить плиту» не слишком трудоёмки — вы всё равно закончите раньше своего помощника и будет просто ждать, когда он заверщит варку риса. В реальной жизни резон может быть как в том, чтобы взять повару ассистента, так и в том, чтобы заменить повара более расторопным товарищем :)

Так или иначе, и в первом случае, и во втором — мы имеем дело с конкурентным подходом, но в первом случае все стадии готовки выполняются одним исполнителем — и это называется псевдопараллельным исполнением (мы режем лук и помидоры попеременно — и благодаря этому получаем горку нарезанных овощей почти синхронно — и можем в любой момент получить неполный, но логически консистентный результат своей работы в виде стопки овощей поменьше), а во втором случае исполнителей двое, и они оба выполняют какую-то часть работы — и здесь исполнение смещанное: оно не полностью параллельное, но и не полностью псевдопараллельное.

Если же обобщить, то потоки исполнения — это инструмент, позволяющий вам гибко организовать вычислительную работу посредством деления её на участки, выполняемые друг относительно друга параллельно, псевдопараллельно или смешанным образом. Потоки исполнения как инструмент позволяют вам решать задачу существенно более гибко, нежели в условиях, например, «чистого DOS» на древнем IBM PC, когда у вас попросту не было альтернативы выполнению работы как одного набора последовательно исполняемых инструкций — последовательных вопреки отсутствию реальной приаязки начала исполнения одной инструкции к завершению исполнения другой.

Когда-то у распространённых «домашних» компьютеров были однозадачные операционные системы и одноядерные процессоры — и тогда всё было именно так: вы понимали, что вот это вычисление результата математического уравнения можно выполнять и не последовательными шагами, но не могли написать программу, которая сделала бы это как-то иначе: не было средств, не было инструментов.

В современной нам действительности есть инструменты очень быстрого «переключения» между разными работами с целью получения их результата примерно в одно и то же время (вспомните пример с переключениями между резкой лука и «шинковкой» помидоров), есть услужливые «исполнители» в виде физических ядер процессоров и даже целых вычислительных узлов, всегда готовые прийти вам на помощь и выполнить часть работы по «варке риса» абсолютно в тот же самый момент времени, когда вы «чистите лук», есть инструменты, позволяющие синхронизировать выполнение разных участков задания по «варке супа»: например, можно подождать всех исполнтелей для того, чтобы объединить результаты их работы — и получить один большой общий результат (ссыпать всё готовое в кастрюлю).
Наличие этих инструментов, позволяет организовать работы в форме совокупности потоков исполнения, но самое важное, а именно методику эффективного решения задачи — вам всё равно придётся выбирать самому, руководствуясь некими общими подходами, так называемыми методологиями. И здесь у вас есть три классических выбора: либо взять готовое чужое решение, положившись на компетенцию другого программиста или best practice, либо придумать это решение полностью самостоятельно, либо сочетать оба подхода, ападптируя чужие решения и применяя свои.

«Да, а чем же отличается многозадачность от многопоточности» — спросите вы. На то ответ простой: с точки зрения технической разные задачи выполняются максимально изолированно друг от друга — подобно поварам на одной кухне, готовящим салаты для разных посетителей ресторана, потоки/нити же — это участки работы в рамках одной задачи, которые выполняются параллельно или псевдопараллельно. При этом такие участки работы выполняются на одном «разделочном столе», оперируют овощами из одной и той исходной комбинации продуктов, а в качестве управляющего их выполнением механизма выступает один и тот же «повар» — он же главный поток или главная нить задачи. Таким образом, задачи — это контейнеры для потоков исполнения, но в вырожденном случае задача сама может быть потоком исполнения, если внутри неё не создаются потоки/нити

На сегодня всё :)

В следующий раз мы поговорим о созданных человечеством методиках использования описанного мной сегодня инструмента — многопоточности (задачи — тоже потоки исполнения, поэтому я предпочитаю говорить всё-таки о многопоточности в общем смысле этого слова). Ну и о тех словечках-паразитах, которые, к сожалению, сопутствуют существованию любой методологии («сборника методик, науки о методиках») и без знания которых вам не удастся понять ваших коллег.

0 комментариев