2013-09-15 3 views
1

(вопрос приводит к двум вопросам)C++ 11 многопоточности замки и атомные примитивы

В C++ 11 являются замки атомное, что один может быть определенно уверены две нити не может получить блокировку в то же время ?

Если ответ на вышеуказанный вопрос «да», то какова цель блокировки с двойной проверкой?

Наконец, зачем нам нужен std :: lock(), когда мы могли бы просто использовать атомный примитив (который, очевидно, мы знаем, это atomic) и установите его в 1 или 0 в зависимости от того, была ли блокировка приобретенных или нет?

ответ

4

Запутанная идея блокировки с двойной проверкой является для не получить блокировку при проверке состояния и приобретать только замок в маловероятном состоянии. Однако, чтобы заставить это работать, требуется еще немного синхронизации, что довольно легко сделать неправильно. Поскольку первичное использование блокировки с двойной проверкой - это инициализация синглетонов (которые являются плохими идеями сами по себе) и постоянные объекты, разделяемые по потокам, C++ 11 фактически реализует поточную безопасную инициализацию функций локальных объектов static. Это должно устранить большинство случаев, когда люди пытаются получить двойную проверку права на блокировку.

Помимо этого: точка блокировки мьютекса состоит в том, что максимум один поток может получить блокировку мьютекса, то есть гарантировать, что ни один из двух потоков не сможет получить одну и ту же блокировку. Что касается использования атомной переменной для обозначения чего-то похожего на блокировку, вам нужно знать, что блокировка/разблокировка мьютекса добавляет дополнительную синхронизацию, кроме того, что делается путем изменения атомного значения: недостаточно знать, что только один поток изменяя общее состояние, также необходимо сообщить об изменениях, внесенных в систему. Кроме того, нормальная блокировка может приостанавливать выполнение потока, когда блокировка не может быть получена (хотя я не думаю, что реализация должна выполняться, т. Е. Я думаю, что она может делать оживленное ожидание, используя блокировку вращения).

2

Замки на 100% атомные, если вы не попытаетесь сделать что-то умное, как уничтожить одно, пока кто-то его приобретает.

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

Вы не можете просто заменить замок атомным примитивом, потому что вы можете ждать блокировки. Если вы ждете блокировки, OS перестает работать с этим потоком и тратит свою мощность процессора в другом месте. Если вы сидите на атомарном примитиве, вы держите CPU занятым и не делаете полезные вещи.

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

Кроме того, вы не можете использовать condition_variables с атомарной переменной, то нужна реальная Заблокируйте

2

Да, блокировки C++ 11 являются атомарными: только один поток может иметь блокировку на одном std::mutex за раз. В этом весь смысл мьютекса: обеспечить взаимное исключение.

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

Обычно это используется в какой-то форме одноразовой инициализации и по-прежнему требует синхронизации. Эта синхронизация может быть выполнена с помощью атомики, но ее трудно получить. Я упоминаю, как это сделать в сообщении в блоге, которое я написал (Lazy Initialization and Double Checked Locking with Atomics), но вам часто лучше использовать только местные static объекты, или std::call_once.

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

И наконец, основной точкой std::lock() является блокировка более одного мьютексов за один раз без взаимоблокировки. Обычно вам нужно блокировать взаимные блокировки по одному за раз. Однако, если поток 1 блокирует mutex A, то mutex B, а поток 2 блокирует мьютекс B, а затем mutex A вы можете получить тупик. std::lock() избегает этого, ожидая, пока поток не сможет получить оба мьютекса, не мешая другим потокам блокировать их в среднее время.

Смежные вопросы