При общении с коллегами возник вопрос, на сколько затратна операция использования std::mutex в libstdc++.
В Linux mutex работает через системный вызов futex, который освобождает процессор и добавляет поток в очередь, если мьютекс занят.
Futex - это сокращение над Fast userspace mutex. Смысл в том, что перед тем как выполнять дорогостоящее обращение к ядру, выполняется проверка флага с использованием атомарных операций, и только если мьютекс действительно занят, то текущий вызов будет поставлен в очередь, которую предоставляет и обслуживает ядро.
Всё что сказано выше - пока просто справочная нформация, которую можно легко загуглить. Интересней другое: если флаг мьютекса занят, то выполняется ли при этом так называемый SPIN WAIT wait перед обращением к ядру.
Такой подход используется, например, в библиотеке .Net framework.
Под Windows std::mutex реализован с использованием spin wait, при этом блокировка крутится заданное число циклов (~4000), и уже по истечении этого времени поток засыпает.
Однако, как выяснилось, в libstdc++ механизм spin wait не реализован. В коде разбросаны комментарии вида:
// TODO Spin-wait first.
В мануале также нет информации по поводу spin wait.
Таким образом, до тех пор пока потоки не конлфиктуют за право владения объектом std::mutex, этот механизм работает крайне быстро и только в user space. Но вот когда начинают возникать блокировки, то сразу появляется необходимость обращения к ядру. Такие обращения сами по себе уже вызывают 10 тыс. циклов CPU. Поэтому не безосновательно принято считать использование мьютексов дорогостоящей операций.
При работе с мьютексами не стоит забывать два основных правила:
- Код под блокировкой мьютекса лучше всего свести к минимуму.
- Находясь пдо блокировкой, не стоит вызывать методы "чужих" объектов.