Определение: Поток, также называемый легковесным процессом, - это базовая загрузка ЦП; он компрометирует идентификатор потока, счетчик программ, набор регистров и стек. Поток - это объект внутри процесса, который можно запланировать для выполнения.

Если мы хотим, чтобы процесс мог выполняться на нескольких процессорах одновременно, чтобы использовать преимущества многоядерных систем, процесс должен иметь несколько контекстов выполнения, называемых потоками. Поток - это активный объект, который выполняет часть процесса. Несколько потоков выполняются одновременно друг с другом, что приводит к выполнению единого процесса. Поскольку несколько потоков процесса выполняются одновременно, они должны поддерживать координацию друг с другом. Координация между потоками требуется для совместного использования системных ресурсов, таких как устройства ввода-вывода, память и, конечно же, ЦП. Потоки процесса являются частью одного и того же виртуального адресного пространства (то есть они разделяют все виртуальное физическое отображение), они совместно используют все данные, код и файл. Однако они будут выполнять другую инструкцию, обращаться к другой части адресного пространства или другими способами. Другой поток имеет другой стек, другой регистр указателя стека, другой Программный счетчик и другие регистры. Блок управления процессом (PCB) многопоточного процесса более сложен, чем однопоточный процесс, как показано на изображении ниже. Он содержит всю информацию, которая совместно используется потоками, и отдельный контекст выполнения для всех потоков.

Преимущества многопоточности

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

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

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

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

Операционная система поддерживает потоки с помощью различных абстракций

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

Механизмы синхронизации:

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

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

Поток и создание потоков

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

Для создания потока Биррелл предлагает вызов вилки с двумя параметрами proc и args, где proc - это процедура, которую созданный поток начнет выполнять с аргументы args. Когда поток вызывает вилку, создается новый поток, т.е. создается новая структура данных потока с ее программным счетчиком, инициализированным первой инструкцией аргумента proc, и аргументы args доступны в ее стеке. После завершения выполнения дочернего потока результат возвращается родительскому потоку, для этого нам нужен другой механизм. Биррелл также предлагает вызов Join , который принимает в качестве аргумента идентификатор потока. Когда соединение вызывается из родительского потока после вызова fork с идентификатором дочернего потока в качестве аргумента, он блокирует родительский поток до тех пор, пока дочерний элемент не завершит свое выполнение, вызов соединения также возвращает результат дочернего потока в родительский поток, в этот момент все ресурсы, которые были выделенный дочернему элементу освобождается, и выполнение дочернего потока прекращается.

API взаимного исключения

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

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

API условных переменных

Для представления условной переменной существует структура данных для представления условной переменной. Эта структура данных содержит ссылку на соответствующий мьютекс для реализации wait API и список всех ожидающих потоков для реализации signal и broadcast API. . Существует конструкция wait, которая принимает в качестве аргументов mutex и condition. Существует конструкция Signal, которая принимает условие в качестве аргумента и уведомляет один ожидающий поток о выполнении условия. Также существует конструкция broadcast, которая уведомляет все ожидающие потоки об этом условии.

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

Тупиковые ситуации

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

Предположим, есть два потока T1 и T2. Чтобы выполнить свою задачу, fun1 и fun2, для чего им обоим нужны два ресурса A и B. Может возникнуть ситуация, в которой один поток говорит, что T1 получает ресурс A, а T2 на другом ядре (CPU) получает ресурс B. Поскольку для успешного выполнения соответствующей задачи им нужен другой ресурс, для которого оба потока будут ждать завершения другого потока, чего не произойдет. Мы называем эту ситуацию тупиком.

Предотвращение тупиковых ситуаций

Три распространенных метода предотвращения тупиковых ситуаций:

  1. Порядок блокировок. Порядок блокировок определяется способом получения ресурсов, который может предотвратить взаимоблокировки в некоторых случаях, как описано выше. Упорядочивание блокировок - это простой, но эффективный механизм предотвращения взаимоблокировок. Однако его можно использовать только в том случае, если вы знаете обо всех необходимых блокировках до взятия любой из блокировок. Это не всегда так.
  2. Тайм-аут блокировки. Еще один механизм предотвращения взаимоблокировок - установить тайм-аут для попыток блокировки, что означает, что поток, пытающийся получить блокировку, будет пытаться только определенное время, прежде чем отказаться. Если потоку не удается взять все необходимые блокировки в течение заданного тайм-аута, он выполняет резервное копирование, освобождает все взятые блокировки, ожидает произвольное время и затем повторяет попытку. Случайное время ожидания служит для того, чтобы дать другим потокам, пытающимся воспользоваться теми же блокировками, шанс снять все блокировки и, таким образом, позволить приложению продолжить работу без блокировок.
  3. Обнаружение взаимоблокировок. Обнаружение взаимоблокировок - это более сложный механизм предотвращения взаимоблокировок, предназначенный для случаев, когда упорядочивание блокировок невозможно, а тайм-аут блокировки невозможен. Каждый раз, когда поток принимает блокировку, это отмечается в структуре данных (карте, графике и т. Д.) Потоков и блокировок. Кроме того, всякий раз, когда поток запрашивает блокировку, это также отмечается в этой структуре данных. Когда поток запрашивает блокировку, но запрос отклоняется, поток может пройти по графу блокировок, чтобы проверить наличие взаимоблокировок.

Пользовательские потоки и потоки ядра

Потоки ядра поддерживаются ядром самой ОС. Все современные ОС поддерживают потоки уровня ядра, что позволяет ядру выполнять несколько одновременных задач и / или одновременно обслуживать несколько системных вызовов ядра. В области приложения нет кода управления потоками. Потоки ядра поддерживаются непосредственно операционной системой. Любое приложение можно запрограммировать на многопоточность. Все потоки в приложении поддерживаются в рамках одного процесса.

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

Существует несколько взаимосвязей между потоками уровня пользователя и потоками уровня ядра, из которых наиболее распространены три:

Один к одному: в этой модели каждый поток уровня пользователя связан с потоком уровня ядра. Когда пользовательский процесс создает поток уровня пользователя, поток уровня ядра либо создан, либо уже присутствует.

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

Многие-к-одному. В этой модели все потоки пользовательского уровня отображаются в один поток уровня ядра. На уровне пользователя существует библиотека управления потоками, которая присутствует на уровне пользователя, которая решает, какой из потоков пользовательского уровня будет фактически сопоставлен с потоком ядра.

  • Преимущества: Поскольку все управление осуществляется библиотекой управления потоками, присутствующей на уровне пользователя, все выполняется библиотекой потоков пользовательского уровня, например, планирование, синхронизация, изменение контекста и т. д. Таким образом, ядро ​​не поддерживает не требуется, следовательно, нет ограничений на лимиты, политики и т. д. Кроме того, нам не нужно выполнять частые системные вызовы, что является дорогостоящей задачей.
  • Недостатки: ОС не имеет информации о планировании, выполняемом на уровне пользователя, поэтому ОС может заблокировать весь процесс, если один поток уровня пользователя блокирует ввод-вывод.

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

Резюме

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