C ++ - faq обычно администрируется сообществом C ++, и вы можете прийти и спросить у нас мнение в нашем чате.
Puppy
@DeadMG: Я не знал о C ++ - faq и его этикете, это было предложено в комментарии.
K-балл
2
Где вы слышали, что const означает потокобезопасность?
Mark B
2
@Mark B: Херб Саттер и Бьярн Страуструп говорили об этом в Standard C ++ Foundation , см. Ссылку внизу ответа.
K-балл
ПРИМЕЧАНИЕ ДЛЯ ТЕМ, ЧТО ЗДЕСЬ: настоящий вопрос НЕ в том, constозначает ли это поточно-ориентированное. Это было бы чепухой, иначе это означало бы, что вы могли бы просто продолжить и пометить каждый потокобезопасный метод как const. Скорее, вопрос, который мы действительно задаем, constИМЕЕТСЯ поточно- ориентированным , и именно об этом идет речь.
user541686
Ответы:
132
Я слышал, что это 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), доступные потокам, отличным от текущего потока, если к объектам не осуществляется прямой или косвенный доступ через неконстантные аргументы функции, включаяthis.
который простыми словами говорит, что он ожидает, что операции с constобъектами будут потокобезопасными . Это означает, что Стандартная библиотека не будет вводить гонку за данные, если операции с constобъектами ваших собственных типов также
Состоит полностью из чтения - то есть нет записи -; или
Внутренне синхронизирует записи.
Если это ожидание не выполняется для одного из ваших типов, то его прямое или косвенное использование вместе с любым компонентом стандартной библиотеки может привести к гонке данных . В заключение, constдействительно означает поточно-ориентированную с точки зрения стандартной библиотеки . Важно отметить, что это просто контракт, и компилятор не применяет его. Если вы его нарушите, вы получите неопределенное поведение и останетесь сами по себе. Независимо от того const, присутствует или нет , не будет влиять на генерацию кода --at мере не в отношении гонок данных -.
Значит ли это constтеперь эквивалент Java «с synchronized?
Нет . Не за что...
Рассмотрим следующий чрезмерно упрощенный класс, представляющий прямоугольник:
Функция- членarea является поточно-ориентированной ; не потому const, что он, а потому, что он полностью состоит из операций чтения. Здесь нет операций записи, и для возникновения гонки данных необходима по крайней мере одна запись . Это означает, что вы можете вызывать areaиз любого количества потоков, и вы всегда будете получать правильные результаты.
Обратите внимание, что это не означает, что rectэто потокобезопасный . На самом деле, легко увидеть, как, если вызов должен areaпроисходить в то же время, что и вызов для set_sizeданного rect, то areaможет в конечном итоге вычислить его результат на основе старой ширины и новой высоты (или даже на искаженных значениях) ,
Но это нормально, rectэто не constзначит, что в конце концов даже не ожидается, что он будет потокобезопасным . С const rectдругой стороны, объявленный объект будет потокобезопасным, поскольку запись невозможна (и если вы рассматриваете const_castчто-то изначально объявленное, constвы получаете неопределенное поведение и все).
Так что же тогда это значит?
Давайте предположим - ради аргумента - что операции умножения чрезвычайно дороги, и нам лучше избегать их, когда это возможно. Мы можем вычислить площадь, только если она будет запрошена, а затем кэшировать ее на случай, если в будущем она будет запрошена снова:
[Если этот пример кажется слишком искусственным, вы можете мысленно заменить intего очень большим динамически выделяемым целым числом, которое по своей природе не является потокобезопасным и для которого умножение является чрезвычайно дорогостоящим.]
Функция- членarea больше не является потокобезопасной , теперь она выполняет запись и не синхронизируется внутренне. Это проблема? Вызов areaможет происходить как часть конструктора копирования другого объекта, такой конструктор мог быть вызван некоторой операцией в стандартном контейнере , и в этот момент стандартная библиотека ожидает, что эта операция будет вести себя как чтение в отношении гонок данных. , Но мы пишем!
Как только мы помещаем объект rectв стандартный контейнер - прямо или косвенно - мы заключаем договор со стандартной библиотекой . Чтобы продолжать выполнять записи в constфункции, при этом соблюдая этот контракт, нам необходимо внутренне синхронизировать эти записи:
Обратите внимание, что мы сделали areaфункцию поточно-ориентированной , но она по- rectпрежнему не является поточно-ориентированной . Вызов, areaпроисходящий одновременно с вызовом, set_sizeможет по-прежнему привести к вычислению неправильного значения, поскольку присвоения widthи heightне защищены мьютексом.
Если бы нам действительно нужна была потокобезопасностьrect , мы бы использовали примитив синхронизации для защиты небезопасных потоковrect .
У них заканчиваются ключевые слова ?
Да, они. У них заканчиваются ключевые слова с первого дня.
@Ben Voigt: Насколько я понимаю, спецификация C ++ 11std::string сформулирована таким образом, что уже запрещает COW . Но я не помню подробностей ...
K-балл
3
@BenVoigt: Нет. Это просто предотвратит несинхронизацию таких вещей, т. Е. Небезопасные потоки. C ++ 11 уже явно запрещает COW - однако этот конкретный отрывок не имеет к этому никакого отношения и не запрещает COW.
Puppy
2
Мне кажется, здесь есть логический пробел. [17.6.5.9/3] запрещает «слишком много», говоря «это не должно прямо или косвенно изменять»; он должен сказать: «не должен прямо или косвенно вызывать гонку данных», если только где-то не определена атомарная запись, которая не является «изменением». Но я нигде не могу этого найти.
иногда мне интересно, кто именно (или те, кто непосредственно участвовал) на самом деле отвечал за написание некоторых стандартных абзацев, таких как эти.
const
означает ли это поточно-ориентированное. Это было бы чепухой, иначе это означало бы, что вы могли бы просто продолжить и пометить каждый потокобезопасный метод какconst
. Скорее, вопрос, который мы действительно задаем,const
ИМЕЕТСЯ поточно- ориентированным , и именно об этом идет речь.Ответы:
Это несколько верно ...
Вот что стандартный язык говорит о безопасности потоков:
что является не чем иным, как достаточным условием для возникновения гонки данных :
Стандартная библиотека основана на том , что, идя немного дальше:
который простыми словами говорит, что он ожидает, что операции с
const
объектами будут потокобезопасными . Это означает, что Стандартная библиотека не будет вводить гонку за данные, если операции сconst
объектами ваших собственных типов такжеЕсли это ожидание не выполняется для одного из ваших типов, то его прямое или косвенное использование вместе с любым компонентом стандартной библиотеки может привести к гонке данных . В заключение,
const
действительно означает поточно-ориентированную с точки зрения стандартной библиотеки . Важно отметить, что это просто контракт, и компилятор не применяет его. Если вы его нарушите, вы получите неопределенное поведение и останетесь сами по себе. Независимо от тогоconst
, присутствует или нет , не будет влиять на генерацию кода --at мере не в отношении гонок данных -.Нет . Не за что...
Рассмотрим следующий чрезмерно упрощенный класс, представляющий прямоугольник:
Функция- член
area
является поточно-ориентированной ; не потомуconst
, что он, а потому, что он полностью состоит из операций чтения. Здесь нет операций записи, и для возникновения гонки данных необходима по крайней мере одна запись . Это означает, что вы можете вызыватьarea
из любого количества потоков, и вы всегда будете получать правильные результаты.Обратите внимание, что это не означает, что
rect
это потокобезопасный . На самом деле, легко увидеть, как, если вызов долженarea
происходить в то же время, что и вызов дляset_size
данногоrect
, тоarea
может в конечном итоге вычислить его результат на основе старой ширины и новой высоты (или даже на искаженных значениях) ,Но это нормально,
rect
это неconst
значит, что в конце концов даже не ожидается, что он будет потокобезопасным . Сconst rect
другой стороны, объявленный объект будет потокобезопасным, поскольку запись невозможна (и если вы рассматриваетеconst_cast
что-то изначально объявленное,const
вы получаете неопределенное поведение и все).Давайте предположим - ради аргумента - что операции умножения чрезвычайно дороги, и нам лучше избегать их, когда это возможно. Мы можем вычислить площадь, только если она будет запрошена, а затем кэшировать ее на случай, если в будущем она будет запрошена снова:
[Если этот пример кажется слишком искусственным, вы можете мысленно заменить
int
его очень большим динамически выделяемым целым числом, которое по своей природе не является потокобезопасным и для которого умножение является чрезвычайно дорогостоящим.]Функция- член
area
больше не является потокобезопасной , теперь она выполняет запись и не синхронизируется внутренне. Это проблема? Вызовarea
может происходить как часть конструктора копирования другого объекта, такой конструктор мог быть вызван некоторой операцией в стандартном контейнере , и в этот момент стандартная библиотека ожидает, что эта операция будет вести себя как чтение в отношении гонок данных. , Но мы пишем!Как только мы помещаем объект
rect
в стандартный контейнер - прямо или косвенно - мы заключаем договор со стандартной библиотекой . Чтобы продолжать выполнять записи вconst
функции, при этом соблюдая этот контракт, нам необходимо внутренне синхронизировать эти записи:Обратите внимание, что мы сделали
area
функцию поточно-ориентированной , но она по-rect
прежнему не является поточно-ориентированной . Вызов,area
происходящий одновременно с вызовом,set_size
может по-прежнему привести к вычислению неправильного значения, поскольку присвоенияwidth
иheight
не защищены мьютексом.Если бы нам действительно нужна была потокобезопасность
rect
, мы бы использовали примитив синхронизации для защиты небезопасных потоковrect
.Да, они. У них заканчиваются ключевые слова с первого дня.
Источник : Вы не знаете
const
иmutable
- Херб Саттеристочник
std::string
сформулирована таким образом, что уже запрещает COW . Но я не помню подробностей ...