【问题标题】:c++: Function that locks mutex for other function but can itself be executed in parallelc ++:为其他函数锁定互斥体但本身可以并行执行的函数
【发布时间】:2023-12-26 11:38:01
【问题描述】:

我有一个关于线程安全和互斥锁的问题。我有两个函数可能不会同时执行,因为这可能会导致问题:

std::mutex mutex;

void A() {
    std::lock_guard<std::mutex> lock(mutex);
    //do something (should't be done while function B is executing)
}

T B() {
    std::lock_guard<std::mutex> lock(mutex);
    //do something (should't be done while function A is executing)
    return something;
}

现在的问题是,函数 A 和 B 不应该同时执行。这就是我使用互斥锁的原因。但是,如果从多个线程同时调用函数 B 则完全没问题。但是,互斥锁也阻止了这种情况(我不希望这样)。那么,有没有办法保证 A 和 B 不会同时执行,同时仍然让函数 B 被并行执行多次?

【问题讨论】:

  • 也许使用某种信号量来跟踪每个函数的当前调用者数量?

标签: c++ multithreading parallel-processing mutex


【解决方案1】:

这很可能充满了错误,但由于您没有 C++14,您可以围绕 std::mutex 创建一个锁计数包装器并使用它:

// Lock-counting class
class SharedLock
{
public:
   SharedLock(std::mutex& m) : count(0), shared(m) {}

   friend class Lock;

   // RAII lock
   class Lock
   {
   public:
      Lock(SharedLock& l) : lock(l) { lock.lock(); }
      ~Lock()                       { lock.unlock(); }
   private:
      SharedLock& lock;
   };

private:

   void lock()
   {
      std::lock_guard<std::mutex> guard(internal);
      if (count == 0)
      {
         shared.lock();
      }
      ++count;
   }


   void unlock()
   {
      std::lock_guard<std::mutex> guard(internal);
      --count;
      if (count == 0)
      {
         shared.unlock();
      }
   }

   int count;
   std::mutex& shared;
   std::mutex internal;
};

std::mutex shared_mutex;

void A()
{
   std::lock_guard<std::mutex> lock(shared_mutex);
   // ...
}


void B()
{
   static SharedLock shared_lock(shared_mutex);
   SharedLock::Lock mylock(shared_lock);
   // ...
}

...当然,除非您想深入研究 Boost。

【讨论】:

    【解决方案2】:

    如果 C++14 是一个选项,您可以使用共享互斥锁(有时称为“读写器”互斥锁)。基本上,在函数 A() 内部,您将获得一个唯一的(排他性,“写入器”)锁,而在函数 B() 内部,您将获得一个共享(非排他性,“读取器”)锁。

    只要存在共享锁,其他线程就不能独占获取互斥锁(但可以非独占获取);只要存在独占锁,任何其他线程都无法获取互斥锁。

    结果是可以有多个线程同时执行函数B(),而函数A()的执行阻止了A()B()被其他线程同时执行:

    #include <shared_mutex>
    
    std::shared_timed_mutex mutex;
    
    void A() {
        std::unique_lock<std::shared_timed_mutex> lock(mutex);
        //do something (should't be done while function B is executing)
    }
    
    T B() {
        std::shared_lock<std::shared_timed_mutex> lock(mutex);
        //do something (should't be done while function A is executing)
        return something;
    }
    

    请注意,即使并发执行 B(),也会始终存在一些同步开销,这是否最终会给您带来比使用普通互斥锁更好的性能高度取决于这些函数内部和外部发生的事情 - 总是在提交更复杂的解决方案之前进行测量。

    Boost.Thread 还提供了shared_mutex 的实现。

    【讨论】:

    • 谢谢。不幸的是,我的编译器还不支持 C++ 14。
    • @user1488118:正如我在回答中提到的,“Boost.Thread 还提供了shared_mutex. 的实现。”。将上述代码 sn-p 中的 std:: 替换为 boost:: 就足够了(哦,std::shared_timed_mutex 替换为 boost::shared_mutex)。
    【解决方案3】:

    您可以在 C++14 中选择。
    使用std::shared_timed_mutex

    A 将使用 lockB 将使用 lock_shared

    【讨论】:

      最近更新 更多