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

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

В многопоточной среде элементарные и привычные операции могут таить в себе опасности. Действительно, простейший оператор вида:

i = i + 1;

содержит в себе опасность, если этот оператор записан в функции потока, выполняемой несколькими экземплярами потоков (совершенно типичный случай). Не менее опасен, но менее очевиден по внешнему виду и оператор:

i += 1;

Даже операторы инкремента и декремента (++i и --i), которые в системе команд практически всех типов процессоров выполняются как атомарные и которые являются основой для реализации семафорных операций, в симметричной мультипроцессорной архитектуре перестают быть безопасными. Хуже того, привычные программисту операции стандартной библиотеки и просто синтаксические конструкции языка становятся небезопасными в многопоточной среде. Вот еще два примера:

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

void* memcpy(void* dst, const void* src, size_t length);

2. Операторы присваивания, инициализации или сравнения структурированных объектов данных:

struct X {

 X(const X& y) { ... }

 friend bool operator==(const X& f, const X& s) { ... }

 // оператор присваивания мы не переопределяем, используется

 // присваивание по умолчанию - побайтовое копирование

};

...

X A;

...

X B(А); // потенциальная ошибка

...

B = A; // потенциальная ошибка

if (А == В) { ... } // потенциальная ошибка

Примечание

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

Именно для безопасного манипулирования данными в параллельной среде QNX API и вводятся атомарные операции. Десять атомарных функций делятся на две симметричные группы по виду своего именования и логике функционирования. Все атомарные операции осуществляются только над одним типом данных unsigned int, но, как будет показано далее, это не такое уж и сильное ограничение. Сам объект, над которым осуществляется атомарная операция (типа unsigned int), — это самая обычная переменная целочисленного типа, только описанная с квалификатором volatile.

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

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

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

void atomic_*(volatile unsigned *D, unsigned S);

unsigned atomic_*_value(volatile unsigned *D, unsigned S);

где вместо * должно стоять имя одной из пяти операций (таким алгоритмом и обеспечивается 10 различных атомарных функций):

add — добавить численное значение к операнду;

sub — вычесть численное значение из операнда;

clr — очистить биты в значении операнда (выполняется побитовая операция (*D) &= ~S);

set — установить биты в значении операнда (выполняется побитовая операция (*D) |= S);

toggle — инвертировать биты в значении операнда (выполняется побитовая операция (*D) ^= S);

D — именно тот объект, над которым осуществляется атомарная операция;

S — второй операнд осуществляемой операции.

Перейти на страницу:

Все книги серии High tech

Нет соединения с сервером, попробуйте зайти чуть позже