Понятия потока («нити») и многопоточности

 

Когда говорят о процессах, то тем самым хотят отметить, что операци­онная система поддерживает их обособленность: у каждого процесса имеется свое виртуальное адресное пространство, каждому процессу назначаются свои ресур­сы – файлы, окна, семафоры и так далее. Такая обособленность нужна для того, чтобы защитить один процесс от другого, поскольку они, совместно используя все ре­сурсы ВМ, конкурируют друг с другом. В общем случае процессы могут быть никак не связаны между собой и даже могут принадлежать раз­ным пользователям, разделяющим одну вычислительную машину. Другими сло­вами, в случае процессов ОС считает их совершенно несвязанными и независимыми. При этом именно ОС берет на себя роль арбитра в конкуренции между процессами по поводу ресурсов.

Однако желательно иметь еще и возможность задействовать внутренний парал­лелизм, который может быть в самих процессах. Такой внутренний параллелизм встречается достаточно часто, а его использование позволяет ускорить реализацию процессов. Например, некоторые операции, выполняемые приложением, могут требо­вать для своего исполнения достаточно длительного использования центрального процессора. В этом случае при интерактивной работе с приложением пользова­тель вынужден долго ожидать завершения заказанной операции и не может управ­лять приложением до тех пор, пока операция не выполнится до самого конца. Такие ситуации встречаются достаточно часто, например, при обработке боль­ших изображений в графических редакторах. Если же программные модули, исполняющие такие длительные операции, оформлять в виде самостоятельных «подпроцессов» (так называемых легковесных или облегченных процессов), которые будут выполняться парал­лельно с другими подобными «подпроцессами», то у пользователя по­является возможность параллельно выполнять несколько операций в рамках од­ного приложения (процесса). Такие «подпроцессы» принято называть потоками или «нитями» (thread). «Подпроцессы» (потоки) называют легковесными потому, что операционная система не должна для них организовывать полноценную вирту­альную машину. Потоки не имеют своих собственных ресурсов, они развива­ются в том же виртуальном адресном пространстве, могут пользоваться теми же файлами, виртуальными устройствами и иными ресурсами, что и данный про­цесс. Единственное, что им необходимо иметь, – это процессорный ресурс. В од­нопроцессорной машине потоки разделяют между собой процессорное время так же, как это делают обычные процессы, а в многопроцессорной машине могут выполняться одновременно, если не встречают конкуренции из-за об­ращения к иным ресурсам.

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

В качестве примера использования многопоточности можно привести электрон­ную таблицу или текстовый процессор. Если они были разработаны с учетом возможностей многопоточной обработки, то пользователь может запросить пересчет своего ра­бочего листа или слияние нескольких документов и одновременно продолжать заполнять таблицу или открывать для редактирования следующий документ. Особенно эффективно можно использовать многопоточ-ность для выполнения распределенных приложений: например, многопоточный сервер может парал­лельно выполнять запросы сразу нескольких клиентов.

Итак, сущность «поток» была введена для того, чтобы именно с помощью этих единиц распределять процессорное время между возможными работами. Сущ­ность «процесс» предполагает, что при диспетчеризации нужно учитывать все ресурсы, закрепленные за ним. А при манипулировании потоками можно менять только их контекст, если происходит переключение с одного потока на другой в рам­ках одного процесса. Все остальные вычислительные ресурсы при этом не затра­гиваются. Каждый процесс всегда состоит по крайней мере из одного потока, и только в случае наличия внутреннего параллелизма становится возможным «расще­пление» этого одного потока на несколько параллельных.

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

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

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

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

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