c++ 对具有条件变量的资源的有限访问(计数器):如何避免死锁?

9gm1akwq  于 2023-04-01  发布在  其他
关注(0)|答案(1)|浏览(74)

我有一个多线程代码访问有限的资源(最大并发线程数由max_accesses定义)。该资源可以从代码的几个部分使用,所以我必须提供一个工具来计算和管理访问。
目前,我用下面的方法来做,但我发现在一些随机的情况下会出现死锁:有时候,condition_variable等待,没有人来唤醒它,因为所有的访问线程在决定等待和有效等待之间释放。
我不习惯使用条件变量,我不知道如何修改我的过程来避免这种死锁。我没有一个测试能够以很大的概率重现这个bug。我宁愿在这里寻求帮助,然后避免其他潜在的bug。

std::mutex mutex_counter;
std::mutex mutex_wait;
std::condition_variable cv_wait;
std::size_t num_accesses = 0;
std::size_t max_accesses = 8;

bool acquire()
{
  {
    std::lock_guard<std::mutex> lock_counter(mutex_counter);
    if(num_accesses < max_accesses)
    {
      ++num_accesses;
      return true;
    }
    // If I'm here, the maximum number of accesses is reached, I have to wait
  }
  std::unique_lock<std::mutex> lock_wait(mutex_wait);
  while(true)
  {
    {
      std::lock_guard<std::mutex> lock_counter(mutex_counter);
      if(num_accesses < max_accesses)
      {
        ++num_accesses;
        return true;
      }
    }
    // Sometimes, here num_accesses == 0, because all the accesing threads just released
    // Then it's an infinite wait because there is no remaining thread to notify
    cv_wait.wait(lock_wait);
  }
}

bool release()
{
  std::lock_guard<std::mutex> lock_counter(mutex_counter);
  assert(num_accesses > 0);
  --num_accesses;
  cv_wait.notify_all();
}
yb3bgrhw

yb3bgrhw1#

// Sometimes, here num_accesses == 0, because all the accesing threads just released
// Then it's an infinite wait because there is no remaining thread to notify

FYI:这个问题有一个名字,它被称为“丢失通知”。您可以通过使用 * 相同的互斥对象 * 作为计数器 * 和 * 作为“等待”来防止它:

bool acquire() {
   std::lock_guard<std::mutex> lg{the_one_and_only_mutex};
   while (num_accesses >= max_accesses) {

       // Here, num_accesses STILL is greater than or equal to max_accesses
       // because the_one_and_only_mutex still is locked. No other thread 
       // could have changed it, and no other thread could have notified
       // cv_wait since the last time we tested num_accesses in this loop.

       cv_wait.wait(lg);
   }

   // Here, num_accesses STILL is less than max_accesses because
   // the_one_and_only_mutex still is locked, so no other thread
   // could have changed it since we exited the loop.

   ++ num_accesses;
   return true;
}

bool release() {
  std::lock_guard<std::mutex> lg(the_one_and_only_mutex);
  assert(num_accesses > 0);
  --num_accesses;
  cv_wait.notify_all();
}

相关问题