Резюме: безопасный многопоточный код

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

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

2. В тех случаях, когда функцию могут вызывать несколько потоков, а какой-либо специфический для состояния потока параметр, например счетчик, должен сохранять свое значение в течение промежутков времени, отделяющих один вызов функции от другого, значение параметра состояния должно храниться в TLS или в структуре данных, выделенной специально для этого потока, например, в структуре данных, передаваемой потоку при его создании. Использовать стек для сохранения постоянно хранимых (persistent) значений не следует. Применение необходимой методики при построении безопасных многопоточных DLL иллюстрируют программы 12.4 и 12.5.

3. Старайтесь не создавать предпосылок для формирования условий состязаний наподобие тех, которые возникли бы в программе 7.2 (sortMT), если бы потоки не создавались в приостановленном состоянии. Если предполагается, что в определенной точке программы должно выполняться некоторое условие, используйте ожидание объекта синхронизации для гарантии того, что, например, дескриптор всегда будет ссылаться на существующий поток.

4. Вообще говоря, потоки не должны изменять окружение процесса, поскольку это окажет воздействие на все потоки. Таким образом, поток не должен определять дескрипторы стандартного ввода и вывода или изменять переменные окружения. Это не касается только основного потока, который может вносить такие изменения до создания других потоков.

5. Переменные, разделяемые всеми потоками, должны быть статическими или храниться в глобальной памяти, объявленной с использованием спецификатора volatile, а также должны быть защищены с использованием описанных ниже механизмов синхронизации.

Объекты синхронизации обсуждаются в следующем разделе. Приведенных в нем объяснений вам будет достаточно для того, чтобы разработать простой пример системы производитель/потребитель (producer/consumer).

Объекты синхронизации потоков

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

1. Поток, выполняющийся в контексте одного процесса, может дожидаться завершения другого процесса с использованием функции ExitProcess путем применения к дескриптору процесса функций ожидания WaitForSingleObject или WaitForMultipleObject. Тем же способом поток может организовать ожидание завершения (с помощью функции ExitThread или выполнения оператора return) другого потока.

2. Блокировки файлов, предназначенные для частного случая синхронизации доступа к файлам.

Windows предоставляет четыре других объекта, предназначенных для синхронизации потоков и процессов. Три из них — мьютексы, семафоры и события — являются объектами ядра, имеющими дескрипторы. События используются также для других целей, например, для асинхронного ввода/вывода (глава 14).

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

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

Предостережение

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

Рассмотрение двух других объектов синхронизации — таймеров ожидания и портов завершения ввода/вывода — отложено до главы 14. Эти типы объектов требуют использования методик асинхронного ввода/вывода Windows, которые описываются в указанной главе.

Cоздание безопасного многопоточного кода в Unity / Валентин Симонов, Антон Яковлев, Unity


Похожие статьи.

Понравилась статья? Поделиться с друзьями: