LINUX.ORG.RU

condition_variable без мьютекса

 ,


2

4

Привет. Надо такое: есть некоторое количество потоков (на стадии компиляции количество неизвестно, в рантайме количество изменяется), нужно их всех усыпить и разбудить из вне в нужный момент. condition_variable - нафиг мне там мьютекс не нужен. Заюзать барьеры - можно, но я тут покумекал, есть свои минусы и лишний гемор. Нужно просто в ждущих потоках сделать wait(), а в управляющем wakeup(), и все ждущие потоки просыпаются, поработали и пошли на новый круг. Без всяких счетчиков - если кто-то затормозил на прошлом круге и не успел сделать wait(), то следующий круг просто пропустил.

★★

Мьсе ищет семафор. Вообще шаблон как таковой называется барьер.

pon4ik ★★★★★
()

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

pon4ik ★★★★★
()

Но данные то с которыми работают потоки одна фигня придётся защищать, чет в твоей постановке нечисто в общем.

pon4ik ★★★★★
()

Ага, начинаю догонять. Тебе нужна локфри очередь событий. Алгоритм примерно такой:

Если разгребли всю очередь - спим на cv, если проснулись то гребём её до конца. Кто проснулся и ему нечего грести - сразу о5 засыпает.

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

Разделяемый объект имеют свою синхронизацию на базе shared_mutex, т.е. даже блокировки то не будет. Кстати, барьеры/семафоры не завезли еще в libstdc++, они с буста в стандарт идут?

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

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

pon4ik ★★★★★
()

в рантайме количество изменяется

Если ты собрался экономить на мьютексах, то в чём былинный смысл делать потоков больше чем есть ядер?

pon4ik ★★★★★
()
Ответ на: комментарий от pavlick

Вроде бы в std::latch и std::barrier, которые вошли в C++20, нет метода wakeup который нужен вам в главном потоке. Там только arrive/wait и arrive_and_wait для рабочих потоков. Пробуждаются рабочие потоки автоматически при достижении счетчиком нуля.

В чем проблема сделать нужный вам лисапед на std::mutex и std::condition_variable?

eao197 ★★★★★
()
Ответ на: комментарий от pon4ik

Ну так просто архитектура проще выходит, вроде как кинул задание в поток и буди лишь в нужный момент. А брать лишний раз мьютекс, зачем, если он мне не нужен.

pavlick ★★
() автор топика
Ответ на: комментарий от eao197

Ну я условно про wakeup(). Там счетчик можно уменьшать/увеличивать на нужное число. Т.е. создать барьер с запасом (счетчик), регистрировать ждунов, уменьшать счетчик из управляющего потока до нуля.

Сделать можно на CV, просто зачем блокировки, когда они там не нужны. + это весьма нагруженное место будет.

pavlick ★★
() автор топика
Последнее исправление: pavlick (всего исправлений: 1)

https://en.cppreference.com/w/cpp/atomic/atomic/notify_all

Правда мне совершенно неясно как у этого с кроссплатформенностью. под капотом по идее futex на линуксе, какой-то аналог на win8+

А вот под win7 (и наверное много других операционок) стандартную библиотеку с поддержкой этого похоже не сделать.

GPFault ★★
()
Ответ на: комментарий от GPFault

Прикольно, я это как-то пропустил.

Ну так оно же в стд, как оно может быть не кроссплатформенно.

pavlick ★★
() автор топика

Ребят, спасибо.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Там счетчик можно уменьшать/увеличивать на нужное число. Т.е. создать барьер с запасом (счетчик), регистрировать ждунов, уменьшать счетчик из управляющего потока до нуля.

И как вы получите текущее количество ждунов на стандартном std::barrier? Метода для получения этого значения у barrier-а нет, главный поток не будет знать, на сколько именно уменьшать.

Либо я не понял ваши условия, либо у вас на каком-то цикле на ожидание могут встать 10 потоков из 10, а на каком-то 8 из 10, а на каком-то 5 из 10.

eao197 ★★★★★
()
Ответ на: комментарий от pavlick

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

Без обид, но люди которые считают что мьютекс это дорого в их конкретном случае без замеров и требований вызывают умиление.

pon4ik ★★★★★
()
Ответ на: комментарий от eao197

На семафоре сделать надо. Запус потоков ведь контролируется, запустил - инкрементировал atomic_int. Потоки встают на ожидание, нужно будить - делаем release(atomic_int). Могут быть всякие накладки в виде один из потоков тупит на прошлом цикле, а более шустрый запустился дважды, но я и написал вначале - схема с проблемами.

pavlick ★★
() автор топика
Ответ на: комментарий от GPFault

А вот под win7 (и наверное много других операционок) стандартную библиотеку с поддержкой этого похоже не сделать.

Насколько я понимаю, на Win8+ используются https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitonaddress и https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-wakebyaddressall

но для Win7 используется fallback на https://docs.microsoft.com/en-us/windows/win32/sync/condition-variables

Visual Studio 2019 поддерживает Windows 7.

Для WinXP скорее всего не будет уже С++20 компилятора…

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от pon4ik

Да в общем-то согласен. Я вообще хотел изначально отправлять спать через планировщик ОС, а барьеры так не работают похоже (я их не щупал). Искал немного более облегченныую версию CV. Видимо буду делать на CV с мьютексом все же.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

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

pon4ik ★★★★★
()
Ответ на: комментарий от pavlick

Ну так оно же в стд, как оно может быть не кроссплатформенно.

Может быть мало платформ где будет полная оддержка c++20 или на части платформ, где нет нативного аналога futex эта функция будет божественно тормозить

GPFault ★★
()
Ответ на: комментарий от pon4ik

Если у тебя не события которых надо по 10к в секунду на ядро обрабатывать

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

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Да меня тоже смущает тредсейф за шарным мьютексом, но комрад же не делится деталями, только тем, что локи это медленно, поэтому я ему тоже про общие случаи.

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

Прежде чем избавляться от блокировок надо обязательно посчитать их цену.

couldn’t agree more

Stil ★★★★★
()
Ответ на: комментарий от pathfinder

Вполне, положу в загашник. У себя сделал на CV, немного переписал, теперь из КВ.вэйт() поток выходит не с бесполезно занятым мьютексом, а делает нужную работу с разделяемым объектом.

pavlick ★★
() автор топика

пользуй futex

typedef uint32_t small_condition;

#define SMALL_CONDITION_INIT 0

int waitCondition(small_condition * c) {
   do {
      int ret = futex(c, FUTEX_WAIT, 0, NULL, NULL, 0);
      if (ret != EAGAIN) return ret;//error
  } while(!(*c));
  return 0;
}
int signalCondition(small_condition *c) {
  *c = 1;
  return futex(c, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
}
salozar
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.