Занятие 11. Взаимодействие потоков

План занятия:

· Основные принципы взаимодействия потоков

· Основные проблемы взаимодействия потоков. Проблема соревнований

· Критические секции

· Блокирование

Основные принципы взаимодействия потоков

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

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

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

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

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

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

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



Основные проблемы взаимодействия потоков. Проблема соревнований

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

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

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

Рассмотрим основные подходы к решению проблемы соревнований:

Критические секции



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

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

Рассмотрим свойства, которыми должна обладать критическая секция:

Остается ответить на далеко не простой вопрос: «Как нам заставить систему воспринимать несколько операций как одну единую операцию?»

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

Блокирование

Рациональным решением является использование блокировок (locks). Блокировка - это механизм, который не позволяет более чем одному потоку выполнять код критической секции. Использование блокировки сводится к двум действиям: введение и снятие блокировки. В случае блокирования проверяют, не было ли оно уже сделано другим потоком, и если это так, этот поток переходит в состояние ожидания, иначе он вводит блокировки и входит в критическую секцию. После выхода из критической секции поток снимает блокировку.

Так реализуют свойство взаимного исключения, отсюда происходит другое название для блокировки - мьютекс (mutex, сокращение от mutual exclusion). Впрочем, чаще это название обозначает конкретный механизм ОС, реализующей блокировки.



7431060930337485.html
7431097766709648.html
    PR.RU™