std::condition_variable счетчик notify
От: Selavi  
Дата: 16.06.15 21:18
Оценка:
Не удалось нагуглить...

есть код:

std::condition_variable cv;
std::unique_lock<std::mutex> lock(mutex);

while (!terminated)
{
  cv.wait(lock); //1
  {
    //2
  }
}


Если вызвать откуда то cv.notify(), то отработает 2 и все ок. Но если вызвать еще раз cv.notify() во время 2, то вызов пропадет зря и в момент, когда выполнение вернется в 1, то поток остановится и будет ждать cv.notify

Можно, конечно, реализовывать вручную счетчики, но может есть какой нить изящный способ сделать так, чтобы 2 отрабатывало столько раз, сколько будет вызван notify?

Спасибо)
Re: std::condition_variable счетчик notify
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 21:56
Оценка:
Здравствуйте, Selavi, Вы писали:

S>Если вызвать откуда то cv.notify(), то отработает 2 и все ок. Но если вызвать еще раз cv.notify() во время 2, то вызов пропадет зря и в момент, когда выполнение вернется в 1, то поток остановится и будет ждать cv.notify


Изменение условия, для ожидания которого используется CV, должно происходить под тем же мьютексом.

S>Можно, конечно, реализовывать вручную счетчики, но может есть какой нить изящный способ сделать так, чтобы 2 отрабатывало столько раз, сколько будет вызван notify?


Для этого дополнительно нужно реализовывать защиту от spurious wakeup.
Re: std::condition_variable счетчик notify
От: vpchelko  
Дата: 16.06.15 22:06
Оценка:
Здравствуйте, Selavi, Вы писали:

  Скрытый текст
S>Не удалось нагуглить...

S>есть код:


S>
S>std::condition_variable cv;
S>std::unique_lock<std::mutex> lock(mutex);

S>while (!terminated)
S>{
S>  cv.wait(lock); //1
S>  {
S>    //2
S>  }
S>}
S>


S>Если вызвать откуда то cv.notify(), то отработает 2 и все ок. Но если вызвать еще раз cv.notify() во время 2, то вызов пропадет зря и в момент, когда выполнение вернется в 1, то поток остановится и будет ждать cv.notify


S>Можно, конечно, реализовывать вручную счетчики, но может есть какой нить изящный способ сделать так, чтобы 2 отрабатывало столько раз, сколько будет вызван notify?


S>Спасибо)


По сути у тебя получается, типичный single threaded executor service — и ты в него добавляешь, только одну и туже задачу. Может не стоит городить костыли, а немного по другому подойти к задаче?
Сало Украине, Героям Сала
Отредактировано 16.06.2015 22:07 vpchelko . Предыдущая версия .
Re[2]: std::condition_variable счетчик notify
От: Selavi  
Дата: 16.06.15 22:10
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, Selavi, Вы писали:


S>>Если вызвать откуда то cv.notify(), то отработает 2 и все ок. Но если вызвать еще раз cv.notify() во время 2, то вызов пропадет зря и в момент, когда выполнение вернется в 1, то поток остановится и будет ждать cv.notify


EP>Изменение условия, для ожидания которого используется CV, должно происходить под тем же мьютексом.


Значит wait отпускает мютекс, а после notify() захватывает снова и следующий notify() будет ждать, пока управление вновь не вернется к wait?
Мне это не нравится, поскольку уведомляющим потокам придется ждать пока отработает уведомляемый.

А хотелось бы, чтобы уведомляющий поток кинул notify() и занялся своими делами.

Нет ли уже готового стандартного способа?
Re[2]: std::condition_variable счетчик notify
От: vpchelko  
Дата: 16.06.15 22:14
Оценка: -1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Изменение условия, для ожидания которого используется CV, должно происходить под тем же мьютексом.


Вообще-то необязательно, если для изменения и условия использовать атомарную операцию — типа CAS, такое вроде давно используется в lock-free алгоритмах. Готового должно быть полно.

EP>Для этого дополнительно нужно реализовывать защиту от spurious wakeup.


Ну это же обычная проверка условия.

Т.е. получается типа:

cv.wait()

while(atomic_condition) {
// 2
}
Сало Украине, Героям Сала
Отредактировано 16.06.2015 22:35 vpchelko . Предыдущая версия . Еще …
Отредактировано 16.06.2015 22:27 vpchelko . Предыдущая версия .
Отредактировано 16.06.2015 22:18 vpchelko . Предыдущая версия .
Отредактировано 16.06.2015 22:17 vpchelko . Предыдущая версия .
Re: std::condition_variable счетчик notify
От: Vamp Россия  
Дата: 16.06.15 22:15
Оценка: :)
Ты неправильно используешь условную переменную. Это не сигнал, а переменная. А у тебя нету переменной! Оттого и проблемы.
S>Не удалось нагуглить...

S>есть код:


S>[ccode]

S>std::condition_variable cv;
S>std::unique_lock<std::mutex> lock(mutex);

S>while (!terminated)

S>{
S> cv.wait(lock); //1
S> {
S> //2
S>
Да здравствует мыло душистое и веревка пушистая.
Re: std::condition_variable счетчик notify
От: andyp  
Дата: 16.06.15 22:33
Оценка: +1
Здравствуйте, Selavi, Вы писали:

S>Можно, конечно, реализовывать вручную счетчики, но может есть какой нить изящный способ сделать так, чтобы 2 отрабатывало столько раз, сколько будет вызван notify?


Нет. condition variable так не работает. Счетчик и есть изящный способ (он собственно и есть shared state твоих ниток). Инкрементируй его под тем же мьютексом, что ты используешь при ожидании. В цикле каждый раз крутись пока счетчик нулевой



void signal()
{    
   {
     std::unique_lock<std::mutex> lock(mutex);
     counter++;    
   }
   cv.signal();
}


//где-то в твоем коде...
{
   std::unique_lock<std::mutex> lock(mutex);
   while(!counter)
  {
    cv.wait(lock);
  }

  //делай что тебе надо и декрементируй счетчик
  counter--;
}
Отредактировано 16.06.2015 22:39 andyp . Предыдущая версия . Еще …
Отредактировано 16.06.2015 22:35 andyp . Предыдущая версия .
Re[2]: std::condition_variable счетчик notify
От: vpchelko  
Дата: 16.06.15 22:36
Оценка: -1
Здравствуйте, andyp, Вы писали:

  Скрытый текст
A>Здравствуйте, Selavi, Вы писали:

S>>Можно, конечно, реализовывать вручную счетчики, но может есть какой нить изящный способ сделать так, чтобы 2 отрабатывало столько раз, сколько будет вызван notify?


A>Нет. condition variable так не работает. Счетчик и есть изящный способ (он собственно и есть shared state твоих ниток). Инкрементируй его под тем же мьютексом, что ты используешь при ожидании. В цикле каждый раз крутись пока счетчик нулевой



A>

A>void signal()
A>{    
A>   {
A>     std::lock_guard<std::mutex> lock(mutex);
A>     counter++;    
A>   }
A>   cv.signal();
A>}


A>//где-то в твоем коде...
A>{
A>   std::lock_guard<std::mutex> lock(mutex);
A>   while(!counter)
A>  {
A>    cv.wait();
A>  }

A>  //делай что тебе надо и декрементируй счетчик
A>  counter--;
A>}
A>


Здесь mutex ненужен
Вы лепите блокировку — там где без неё много лет прекрасно справляются.
Сало Украине, Героям Сала
Отредактировано 16.06.2015 22:40 vpchelko . Предыдущая версия .
Re[3]: std::condition_variable счетчик notify
От: andyp  
Дата: 16.06.15 22:40
Оценка:
Здравствуйте, vpchelko, Вы писали:

V>Здесь mutex ненужен


Где? Могу и накосячить Не использую пока threading library.
Re[2]: std::condition_variable счетчик notify
От: Selavi  
Дата: 16.06.15 22:43
Оценка:
Здравствуйте, andyp, Вы писали:

  скрыто
A>Здравствуйте, Selavi, Вы писали:

S>>Можно, конечно, реализовывать вручную счетчики, но может есть какой нить изящный способ сделать так, чтобы 2 отрабатывало столько раз, сколько будет вызван notify?


A>Нет. condition variable так не работает. Счетчик и есть изящный способ (он собственно и есть shared state твоих ниток). Инкрементируй его под тем же мьютексом, что ты используешь при ожидании. В цикле каждый раз крутись пока счетчик нулевой



A>

A>void signal()
A>{    
A>   {
A>     std::unique_lock<std::mutex> lock(mutex);
A>     counter++;    
A>   }
A>   cv.signal();
A>}


A>//где-то в твоем коде...
A>{
A>   std::unique_lock<std::mutex> lock(mutex);
A>   while(!counter)
A>  {
A>    cv.wait(lock);
A>  }

A>  //делай что тебе надо и декрементируй счетчик
A>  counter--;
A>}
A>


Не нравится мне этот код
Какой смысл в счетчике, если мы все равно ждем мютекса, чтобы его увеличить?
Re[3]: std::condition_variable счетчик notify
От: andyp  
Дата: 16.06.15 22:48
Оценка:
Здравствуйте, Selavi, Вы писали:

S>Какой смысл в счетчике, если мы все равно ждем мютекса, чтобы его увеличить?


Счетчик shared. Надо ждать. Иначе будут гонки с декрементом. Но проблема на мой взгляд невелика — ждем только в ветке с декрементом. cv.wait отдает мьютекс
Re[3]: std::condition_variable счетчик notify
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 22:58
Оценка:
Здравствуйте, vpchelko, Вы писали:

EP>>Изменение условия, для ожидания которого используется CV, должно происходить под тем же мьютексом.

V>Вообще-то необязательно, если для изменения и условия использовать атомарную операцию — типа CAS, такое вроде давно используется в lock-free алгоритмах.

В таких случаях потеря уведомления обычно не страшна (которая получается из-за изменения состояния без мьютекса), так как используется wait_for с таймаутом, вместо wait.
В крайнем случае notify_one можно делать под мьютексом, но тогда смысла в lock free может и не быть.

V>Готового должно быть полно.


Готового чего?

EP>>Для этого дополнительно нужно реализовывать защиту от spurious wakeup.

V>Ну это же обычная проверка условия.

Которой нет в исходном сообщении ТС
Отредактировано 16.06.2015 23:36 Evgeny.Panasyuk . Предыдущая версия . Еще …
Отредактировано 16.06.2015 23:36 Evgeny.Panasyuk . Предыдущая версия .
Re[4]: std::condition_variable счетчик notify
От: Selavi  
Дата: 16.06.15 23:04
Оценка:
Здравствуйте, andyp, Вы писали:

A>Здравствуйте, Selavi, Вы писали:


S>>Какой смысл в счетчике, если мы все равно ждем мютекса, чтобы его увеличить?


A>Счетчик shared. Надо ждать. Иначе будут гонки с декрементом. Но проблема на мой взгляд невелика — ждем только в ветке с декрементом. cv.wait отдает мьютекс


В таком варианте вообще не нужен счетчик

мы ждем пока отработает задача, чтобы увеличить счетчик...зачем он вообще нужен? Проще сразу делать cv.notify() для запуска очередного рабочего цикла..


Я хочу, чтоб было так:

1) рабочий поток ждет
2) вызывающий поток(1) кидает уведомление
3) рабочий поток начинает выполнять задачу
4) вызывающий поток(1-N) кидает уведомление во время выполнения задачи и выходит из метода уведомления
5) рабочий поток завершает задачу
6) рабочий поток видит, что его жду новые уведомления и для каждого запускает новый рабочий цикл, либо переходит в ждущий режим если нет уведомлений

как вариант — это очередь заданий.
но я надеялся, что есть некий стандартный механизм...
Re[5]: std::condition_variable счетчик notify
От: andyp  
Дата: 16.06.15 23:15
Оценка: +1
Здравствуйте, Selavi, Вы писали:

S>В таком варианте вообще не нужен счетчик


Счетчик нужен, чтобы считать, сколько раз ты позвал cv.signal().

S>мы ждем пока отработает задача, чтобы увеличить счетчик...зачем он вообще нужен? Проще сразу делать cv.notify() для запуска очередного рабочего цикла..


Зачем ждать? Декрементируй счетчик, отдай мьютекс, продолжай заниматься своими делами. Мьютекс отдают после того, как закончили работать с shared state.

S>Я хочу, чтоб было так:


S>1) рабочий поток ждет

S>2) вызывающий поток(1) кидает уведомление
S>3) рабочий поток начинает выполнять задачу
S>4) вызывающий поток(1-N) кидает уведомление во время выполнения задачи и выходит из метода уведомления
S>5) рабочий поток завершает задачу
S>6) рабочий поток видит, что его жду новые уведомления и для каждого запускает новый рабочий цикл, либо переходит в ждущий режим если нет уведомлений

S>как вариант — это очередь заданий.


Очередь — все тоже самое, только shared state будет объект std::queue. Вместо инкремента будет std::queue.push(), std::queue.pop() — декремент, в условии while(queue.empty()

S>но я надеялся, что есть некий стандартный механизм...


Это и есть стнадартный механизм работы с кондварами
Re[3]: std::condition_variable счетчик notify
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 23:26
Оценка: +2
Здравствуйте, Selavi, Вы писали:

S>>>Если вызвать откуда то cv.notify(), то отработает 2 и все ок. Но если вызвать еще раз cv.notify() во время 2, то вызов пропадет зря и в момент, когда выполнение вернется в 1, то поток остановится и будет ждать cv.notify

EP>>Изменение условия, для ожидания которого используется CV, должно происходить под тем же мьютексом.
S>Значит wait отпускает мютекс, а после notify() захватывает снова и следующий notify() будет ждать, пока управление вновь не вернется к wait?

Будет ждать не notify, а следующее изменение состояния. notify можно делать после изменения состояния, не под мьютексом.

S>Мне это не нравится, поскольку уведомляющим потокам придется ждать пока отработает уведомляемый.

S>А хотелось бы, чтобы уведомляющий поток кинул notify() и занялся своими делами.
S>Нет ли уже готового стандартного способа?

Если нужно посчитать именно количество вызовов notify, то нужно завести счётчик, чтобы отфильтровать spurious wakeup.
Если при этом требуется не захватывать мьютекс на стороне уведомляющего — то нужно использовать атомарный счётчик, плюс вместо wait нужен wait_for с таймаутом, так как помимо spurious wakeup теперь возможны потерянные уведомления (между проверкой условия и wait'ом, условие может поменяться, так как оно теперь не под мьютексом).
Re[4]: std::condition_variable счетчик notify
От: uzhas Ниоткуда  
Дата: 17.06.15 07:57
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>плюс вместо wait нужен wait_for с таймаутом, так как помимо spurious wakeup теперь возможны потерянные уведомления (между проверкой условия и wait'ом, условие может поменяться, так как оно теперь не под мьютексом).


хороший воркараунд ) но все же лучше учить всех использовать cv в паре с mutex, даже если условие допускает атомарный доступ. что я и советую сделать TC.
Re[5]: std::condition_variable счетчик notify
От: Selavi  
Дата: 17.06.15 09:03
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>>плюс вместо wait нужен wait_for с таймаутом, так как помимо spurious wakeup теперь возможны потерянные уведомления (между проверкой условия и wait'ом, условие может поменяться, так как оно теперь не под мьютексом).


U>хороший воркараунд ) но все же лучше учить всех использовать cv в паре с mutex, даже если условие допускает атомарный доступ. что я и советую сделать TC.


ну тогда видимо нужно вешать счетчик на другой мютекс, чтобы уведомляющий поток не ждал, пока отработается задача в рабочем потоке.
Re[6]: std::condition_variable счетчик notify
От: uzhas Ниоткуда  
Дата: 17.06.15 09:20
Оценка: +2
Здравствуйте, Selavi, Вы писали:

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


конечно, вам нужно отпускать мьютекс как можно быстрее. тогда нотификация будет пролетать быстро
длинные задачи делайте без участия этого мьютекса (отпустите его)
только когда вы закончите делать задачу вам надо будет уменьшить счетчик — делайте это аккуратно, т.к.
1) менять счетчик надо под мьютексом
2) если счетчик после уменьшения все еще положительный, то надо запустить следующую задачу без ожидания на cv
3) если счетчик нулевой, то, не отпуская мьютекс, надо уйти в cv.wait(mutex) =). отпустить, конечно, можно, но потом надо где-то снова его залочить, проверить счетчик и, если тот нулевой, уйти в cv.wait(mutex); иначе приступить к след. задаче

успехов
Отредактировано 17.06.2015 9:25 uzhas . Предыдущая версия .
Re[5]: std::condition_variable счетчик notify
От: Evgeny.Panasyuk Россия  
Дата: 17.06.15 11:00
Оценка:
Здравствуйте, uzhas, Вы писали:

EP>>плюс вместо wait нужен wait_for с таймаутом, так как помимо spurious wakeup теперь возможны потерянные уведомления (между проверкой условия и wait'ом, условие может поменяться, так как оно теперь не под мьютексом).

U>хороший воркараунд ) но все же лучше учить всех использовать cv в паре с mutex, даже если условие допускает атомарный доступ. что я и советую сделать TC.

Изменение условия может быть долгим, например если это какое-нибудь STM. В этом случае можно под мьютексом делать только notify_one — тогда пропущенных уведомлений не будет.
Есть ещё вариант без CV — просто sleep_for/yield + атомарное изменение состояния/счётчика.

У каждого варианта какие-то свои преимущества/недостатки. Например в случае изменения счётчика/состояния под мьютексом — уведомляемый поток может просыпаться очень редко, практически только на уведомлениях (+ редкие spurious wakeup).
В случае же с wait_for/sleep_for — получаем либо слишком частые просыпания, либо возможность нарваться на слишком долгую реакцию при пропущенном уведомлении.
Re[3]: std::condition_variable счетчик notify
От: B0FEE664  
Дата: 17.06.15 11:22
Оценка:
Здравствуйте, Selavi, Вы писали:

S>А хотелось бы, чтобы уведомляющий поток кинул notify() и занялся своими делами.


S>Нет ли уже готового стандартного способа?

Стандартного — нет. Посмотрите сюда
Автор: B0FEE664
Дата: 29.09.14
.
И каждый день — без права на ошибку...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.