Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Для решения этой проблемы введем новый оператор длинная стрелка --->Это из этой серии что-то?
AnRWLock<Counter> c;
c--->set(2);
c--->inc();
c-->somethingElse();
И не будет ли в реальном проекте куча такого кода?Ну если в реальном проекте не смущает использование "." или "->" в приведенных операциях, то я не вижу ничего зазорного использовать --->. Но это — на любителя, конечно.
С COW — вообще отдельная песня.Да, это — не панацея. Собственно, в статье детально изложены плюсы и минусы использования.
Ну если в реальном проекте не смущает использование "." или "->" в приведенных операциях, то я не вижу ничего зазорного использовать --->.Ну вот видите, вы сами не заметили, в чем подвох. А дело в том, что такой код будет большую часть времени заниматься захватом и возвращением мьютексов в ядре ОС, вместо того, чтобы выполнять свои прямые обязанности. Поэтому, я считаю, некоторые вещи лучше делать явно.
А дело в том, что такой код будет большую часть времени заниматься захватом и возвращением мьютексов в ядре ОС, вместо того, чтобы выполнять свои прямые обязанности.Не совсем понятно, как предлагается работать: без мьютексов? Или имелось в виду частое использование? Для этого описаны способы, как оптимизировать использование.
Поэтому, я считаю, некоторые вещи лучше делать явно.Ну на этот счет есть разные мнения. Возможно, что вам подойдет С-подход: там надо все делать явно. Вы скорее всего подразумеваете, что scoped-локи — это тот уровень автоматизма, дальше которого не стоит продвигаться. Я так не считаю, т.к. чем больше будет делать за тебя компилятор — тем лучше. Если возникнет проблема производительности, то всегда имеется возможность заоптимизировать. Более того, современные реализации мьютексов лезут в ядро только когда объект действительно занят, причем достаточно длительное время.
{
Mutex m;
Lock l(m);
m.unlock();
}
и после вызова деструктора Lock мы получаем неопределённое поведение.Почему?
Или этот мьютекс будет на святом духе работать?На атомарном счетчике ссылок, как все изветсные мне реализации.
Вы что — верите разработчика библиотек?Да. И это меня еще не подводило.
Кому-то ещё охота возиться с кучей локов и мутексов, корректно и быстро работать с которыми получается практически никогда?У меня практически всегда удается корректно и быстро работать. Что я делаю не так?
Уж либо использовать атомарные примитивы, либо строить приложение на базе более высокоуровневых конструкций вроде тредпулов с фьючерами, очередей с производителями/потребителями или fork/join (одно не исключает другого).Очень сильно зависит от задач. Если правильно шарить данные, то никаких проблем не бывает. И мне очень интересно узнать, как реализовать, например, консистентное кеширование данных в памяти с использованием фьючерсов или тредпулов.
как реализовать, например, консистентное кеширование данных в памяти
На самом деле нет. В этом «идеале» нет composability.Судя по всему, вы пропустили слово «практически». Я не настаиваю на том, что это — идеал, но очень близко к нему. Этот пример приведен в качестве затравки. Более того, моя статья посвящена несколько другим аспектам, где этот недостаток («composability») сильно сглажен (см. комментарий).
Пример: мы пишем сервер, поток, принявший запрос, определяет по round robin алгоритму, какому именно потоку делигировать выполнение запроса, затем кладет в запрос в входящую очередь этого потока, далее, обработка запросов может выполняться поэтапно несколькими потоками используя pipeline парралелизм.Выше приводился пример про кешировние, когда использование такого подхода для доступа к данным приводит к дополнительным накладным расходам.
В общем, цель, это обычно баланс между fine и coarse granularity, а данный подход как раз фокусируется только на первом. Это полхо.С тем же успехом можно сказать, что я в своей статье использовал С++ и ничего не сказал про Java, а это плохо. Или что ничего не сказал о conditional variable или атомарных конструкциях. Странный аргумент. Вместе с тем, подход не исключает использование coarse granularity. Для этого всего лишь нужно создать один An(RW)Lock объект, не включающий в себя другие An(RW)Lock объекты.
Если один поток ждет данные а другой — предоставляет, то, если оба потока выполняются, никакого переключения контекста не будет, мьютекс там, либо атомарная переменная.
Если поток пытается захватить мьютекс, а он уже захвачен, то это приведет к переключению контекста. В случае lock free очереди — переключения контекста не будет, поток потратит свой квант времени на busy wait на этой самой очереди. На практике, накладные расходы появляются тогда, когда очереди сильно выростают, начинают жрать память и приводить к кэш промахам.
Вы рассматриваете варинат std::queue + condition variable вместо специализированой очереди?
При высокой загрузке в очереди постоянно будут элементы, поэтому consumer не будет ждать, он будет просто каждый раз извлекать новый элемент без ожидания. К тому же, ждать элементов в очереди можно с помощью busy wait, если так важна латентность.
То, что исползуя очередь, мы жертвуем латентностью, ради увеличения пропускной способности — не секрет. Я только не пойму, откуда возьмутся решедулинги и почему они испортят производительность.
Кому-то ещё охота возиться с кучей локов и мутексов, корректно и быстро работать с которыми получается практически никогда?
Уж либо использовать атомарные примитивы, либо строить приложение на базе более высокоуровневых конструкций вроде тредпулов с фьючерами, очередей с производителями/потребителями или fork/join (одно не исключает другого).
P.S. мне кажется, наша с вами переписка могла дать много новых знаний программистам, только начинающим изучать многопоточностьПредлагаю оформить это в виде отдельной статьи, со сравнением и результатами измерений на сценариях, близких к реальным. )
в очередь запихиваем нечто и потом ожидаем ответа
практически
RWMutex::rlock
Key-value: extracting key: users
RWMutex::wlock
Counter::inc: 4
RWMutex::wunlock
RWMutex::runlock
Полезные идиомы многопоточности С++