Я слышу это const
средства потокобезопасной в C ++ 11. Это правда?
Это значит const
теперь эквивалент Джава«s synchronized
?
Они заканчиваются ключевые слова?
Я слышу это
const
средства потокобезопасной в C ++ 11. Это правда?
это в некотором роде правда…
Это то, что Стандартный язык должен сказать о безопасности потока:
[1.10 / 4]
Две оценки выражений конфликт если один из них изменяет ячейку памяти (1.7), а другой обращается или изменяет ту же ячейку памяти.[1.10 / 21]
Выполнение программы содержит гонка данных если он содержит два конфликтующих действия в разных потоках, по крайней мере одно из которых не является атомарным, и ни одно из них не происходит раньше другого. Любая такая гонка данных приводит к неопределенному поведению.
что является не чем иным, как достаточным условием для гонка данных происходить:
Стандартная библиотека опираясь на это, пойдем немного дальше:
[17.6.5.9/1]
Этот раздел определяет требования, которым должны соответствовать реализации, чтобы предотвратить гонки данных (1.10). Каждая стандартная библиотечная функция должна отвечать каждому требованию, если не указано иное. Реализации могут предотвратить скачки данных в случаях, отличных от указанных ниже.[17.6.5.9/3]
Стандартная библиотечная функция C ++ не должна прямо или косвенно изменять объекты (1.10), доступные для потоков, отличных от текущего потока, если к объектам не обращаются прямо или косвенно через функциюConst аргументы, в том числеthis
,
который простыми словами говорит, что ожидает операции на const
объекты для потокобезопасной. Это означает, что Стандартная библиотека не будет вводить гонку данных, пока операции на const
объекты ваших собственных типов либо
Если это ожидание не выполняется для одного из ваших типов, то используйте его прямо или косвенно вместе с любым компонентом Стандартная библиотека может привести к гонка данных. В заключение, const
значит потокобезопасной от Стандартная библиотека точка зрения. Важно отметить, что это всего лишь контракт и это не будет выполняться компилятором, если вы нарушите его, вы получите неопределенное поведение и ты сам по себе. Будь то const
присутствует или нет не повлияет на генерацию кода — по крайней мере, не в отношении гонки данных—.
Это значит
const
теперь эквивалент Джава«ssynchronized
?
нет. Не за что…
Рассмотрим следующий чрезмерно упрощенный класс, представляющий прямоугольник:
class rect {
int width = 0, height = 0;
public:
/*...*/
void set_size( int new_width, int new_height ) {
width = new_width;
height = new_height;
}
int area() const {
return width * height;
}
};
член-функция area
является потокобезопасной; не потому что его const
, но потому что он состоит исключительно из операций чтения. Нет вовлеченных записей, и для гонка данных происходить. Это означает, что вы можете позвонить area
из столько потоков, сколько вы хотите, и вы будете получать правильные результаты все время.
Обратите внимание, что это не значит, что rect
является потокобезопасной. На самом деле, легко увидеть, как если бы звонок area
должно было произойти в то же время, что вызов set_size
на данный rect
, затем area
может в итоге вычислить свой результат на основе старой ширины и новой высоты (или даже искаженных значений).
Но это нормально, rect
не const
так что его даже не ожидается потокобезопасной в конце концов. Объект объявлен const rect
с другой стороны, будет потокобезопасной поскольку никакие записи невозможны (и если вы рассматриваете const_cast
что-то первоначально заявленное const
тогда вы получите неопределенное-поведение и это все).
Так что же это значит?
Давайте предположим — ради аргумента — что операции умножения чрезвычайно дороги, и мы лучше их избегать, когда это возможно. Мы могли бы вычислить область, только если она запрашивается, и затем кэшировать ее на случай, если она будет запрошена снова в будущем:
class rect {
int width = 0, height = 0;
mutable int cached_area = 0;
mutable bool cached_area_valid = true;
public:
/*...*/
void set_size( int new_width, int new_height ) {
cached_area_valid = ( width == new_width && height == new_height );
width = new_width;
height = new_height;
}
int area() const {
if( !cached_area_valid ) {
cached_area = width;
cached_area *= height;
cached_area_valid = true;
}
return cached_area;
}
};
[Если этот пример кажется слишком искусственным, вы могли бы мысленно заменить int
по очень большое динамически размещаемое целое число который по своей сути не потокобезопасной и для которых умножения являются чрезвычайно дорогостоящими.]
член-функция area
больше не потокобезопасной, это делает записи сейчас и не внутренне синхронизированы. Это проблема? Призыв к area
может случиться как часть копия-конструктор другого объекта, такого конструктор мог быть вызван какой-то операцией на стандартный контейнер, и в этот момент стандартная библиотека ожидает, что эта операция будет вести себя как читать в отношении гонки данных. Но мы делаем записи!
Как только мы положим rect
в стандартный контейнер —прямо или косвенно — мы вступаем в контракт с Стандартная библиотека. Чтобы продолжать делать записи в const
пока мы выполняем этот контракт, нам нужно внутренне синхронизировать эти записи:
class rect {
int width = 0, height = 0;
mutable std::mutex cache_mutex;
mutable int cached_area = 0;
mutable bool cached_area_valid = true;
public:
/*...*/
void set_size( int new_width, int new_height ) {
if( new_width != width || new_height != height )
{
std::lock_guard< std::mutex > guard( cache_mutex );
cached_area_valid = false;
}
width = new_width;
height = new_height;
}
int area() const {
std::lock_guard< std::mutex > guard( cache_mutex );
if( !cached_area_valid ) {
cached_area = width;
cached_area *= height;
cached_area_valid = true;
}
return cached_area;
}
};
Обратите внимание, что мы сделали area
функция потокобезопасной, но rect
все еще не потокобезопасной. Вызов area
происходит в то же время, что вызов set_size
может все еще в конечном итоге вычислить неправильное значение, так как назначения width
а также height
не защищены мьютексом.
Если бы мы действительно хотели потокобезопасной rect
мы бы использовали примитив синхронизации для защиты без потокобезопасной rect
,
Они заканчиваются ключевые слова?
Да. У них кончились ключевые слова с первого дня.
Источник: Ты не знаешь const
а также mutable
— Херб Саттер
Других решений пока нет …