Блокируйте несколько потоков вокруг атомной переменной. условные_вариабельны или что-то еще?

#multithreading #c 11

Вопрос:

Я пытаюсь разработать систему, в которой у меня будет несколько потоков. Все эти потоки одновременно используют общую переменную (которая является атомной), и когда каждый поток запускается, он увеличивает эту переменную, а перед завершением этого потока уменьшает эту переменную. Я хотел бы, чтобы поток блокировался, если текущее значение этой переменной превышает определенное предельное значение. Простой psuedocode таков:

 std::atomic<int> var;
int limit = 3; //limit value
void doWork() //This method is run as multiple thread simultaneously
{
  if(var.load() < limit) 
  {
     //block until this condition is met
  }
  //increment
  var  ;
  //Do some work
  .....
  //Decrement before leaving
  var--
}
 

Теперь мне на ум приходят два подхода. Первый подход-спать во время var.load < limit сна, но это выглядит неоптимальным. Второй подход заключается в использовании conditional_variable . Я использую этот подход ниже, но я считаю, что здесь у меня есть две проблемы

  1. Первый поток будет заблокирован при cv.wait(lk, []{возврат var.load() ;
  2. Несколько потоков не будут выполнять работу, только один будет выполнять работу, в то время как другие будут заблокированы.

Я был бы признателен, если бы кто-нибудь мог подсказать, как я могу удалить эти дефекты из приведенного ниже кода, или если есть лучший подход ?

 std::condition_variable cv;
std::mutex cv_m;
std::atomic<int> var;
int limit = 3; //limit value
void doWork()
{
  std::unique_lock<std::mutex> lk(cv_m);
  std::cerr << "Waiting... n";
  cv.wait(lk, []{
         return var.load() < limit
        });
  //Increment var
   var  ;
  //Do some Work
  ...
  //Decrement Work
  var--
  cv.notify_all();
}
 

Комментарии:

1. Поведение, которое вы описываете, очень похоже на поведение семафора: en.m.wikipedia.org/wiki/Semaphore_(программирование)

2. Вы должны разблокировать после var и до «Выполнения некоторой работы». И нет, первый поток не будет блокировать — вспомните, что condition_variable::wait на самом деле не ждет, если предикат уже удовлетворен; он ждет только в том случае, если он ложен, и до тех пор, пока он не станет истинным.