C++ 同时锁定两个互斥锁

C++ 同时锁定两个互斥锁,c++,multithreading,mutex,C++,Multithreading,Mutex,我正在尝试实现一个多入多出的线程间通道类。我有三个互斥锁:full在缓冲区满时锁定空在缓冲区为空时锁定th在其他人修改缓冲区时锁定。我的单IO程序看起来像 operator<<(...){ full.lock() // locks when trying to push to full buffer full.unlock() // either it's locked or not, unlock it th.lock() ... emp

我正在尝试实现一个多入多出的线程间通道类。我有三个互斥锁:
full
在缓冲区满时锁定<代码>空在缓冲区为空时锁定
th
在其他人修改缓冲区时锁定。我的单IO程序看起来像

operator<<(...){
  full.lock()        // locks when trying to push to full buffer
  full.unlock()      // either it's locked or not, unlock it
  th.lock()
  ...
  empty.unlock()     // it won't be empty
  if(...)full.lock() // it might be full
  th.unlock()
operator>>(...){
  // symmetric
}
运算符(…){
//对称的
}

这对于单IO来说是非常好的。但对于多个IO,当使用者线程解锁
full
时,所有提供程序线程都将关闭,只有一个线程将获得
th
,缓冲区可能会因为该单个线程而再次满,而不再进行完全检查。当然,我可以再次添加
full.lock()
,但这是无止境的。是否可以同时锁定
full
th
?我确实看到了一个类似的问题,但我不认为顺序是这里的问题。

不,您不能同时锁定两个互斥锁,但您可以对等待的线程使用
std::condition\u变量
,并在完成后调用
notify\u one


有关更多详细信息,请参阅。

否,您不能同时锁定两个互斥锁,但可以对等待的线程使用
std::condition\u变量
,并在完成后调用
notify\u one

有关更多详细信息,请参阅。

是,使用
std::lock(满,th),这可以避免一些死锁

例如: 线程1:

线程2:

th.lock();
full.lock();
std::lock(th, full);
这可能会导致死锁,但以下情况不会导致死锁:

线程1:

std::lock(full, th);
线程2:

th.lock();
full.lock();
std::lock(th, full);
是,使用
std::lock(满,th),这可以避免一些死锁

例如: 线程1:

线程2:

th.lock();
full.lock();
std::lock(th, full);
这可能会导致死锁,但以下情况不会导致死锁:

线程1:

std::lock(full, th);
线程2:

th.lock();
full.lock();
std::lock(th, full);

不,不能自动锁定两个互斥锁

此外,看起来您在一个线程中锁定互斥锁,然后在另一个线程中解锁它。这是不允许的


对于这个问题,我建议切换到条件变量。请注意,将一个互斥体与多个条件变量关联是非常好的。

不,您不能自动锁定两个互斥体

此外,看起来您在一个线程中锁定互斥锁,然后在另一个线程中解锁它。这是不允许的


对于这个问题,我建议切换到条件变量。请注意,将一个互斥体与多个条件变量关联是非常好的。

您试图实现的功能性需要类似于SystemV信号量的东西,在SystemV信号量上的一组操作可以原子化应用。在您的情况下,您将有3个信号灯:

  • 信号量1-锁定,初始化为0
  • 信号量2-可用数据的计数器,初始化为0
  • 信号量3-可用缓冲区计数器,初始化有多少缓冲区
然后,按下操作将使该组锁定:

  • 检查信号量1是否为0
  • 将信号量1增加+1
  • 将信号量2增加+1
  • 将信号量3减少-1
然后

  • 将信号量1减少-1
解锁。然后,要提取数据,第一个组将更改为:

  • 检查信号量1是否为0
  • 将信号量1增加+1
  • 将信号量2减少-1
  • 将信号量3增加+1

解锁与以前相同。使用互斥量(这是一种特殊情况的信号量)很可能无法以这种方式解决问题。首先,它们是二进制的,即只有2个状态,但更重要的是API不提供对它们的组操作。因此,您可以为您的平台找到信号量实现,或者使用带有条件变量的单个互斥来向等待的线程发出信号,表示数据或缓冲区可用。

您试图实现的功能性需要类似于System V信号量的东西,在System V信号量中,可以原子地应用信号量上的一组操作。在您的情况下,您将有3个信号灯:

  • 信号量1-锁定,初始化为0
  • 信号量2-可用数据的计数器,初始化为0
  • 信号量3-可用缓冲区计数器,初始化有多少缓冲区
然后,按下操作将使该组锁定:

  • 检查信号量1是否为0
  • 将信号量1增加+1
  • 将信号量2增加+1
  • 将信号量3减少-1
然后

  • 将信号量1减少-1
解锁。然后,要提取数据,第一个组将更改为:

  • 检查信号量1是否为0
  • 将信号量1增加+1
  • 将信号量2减少-1
  • 将信号量3增加+1

解锁与以前相同。使用互斥量(这是一种特殊情况的信号量)很可能无法以这种方式解决问题。首先,它们是二进制的,即只有2个状态,但更重要的是API不提供对它们的组操作。因此,您可以为您的平台找到信号量实现,或者使用带有条件变量的单个互斥体来向等待的线程发出数据或缓冲区可用的信号。

您使用互斥体时出现了问题。不,您不能原子地锁定两个互斥锁,您需要信号量。例如,SystemV信号量可以像inteded一样工作,但这是特定于操作系统的。如果您有一个c++17库,那么我认为您总是希望使用条件变量而不是一些互斥变量。如果您不正确地使用互斥体,那么问题就出在这里。不,不能原子地锁定2个互斥锁,您需要信号量。例如,System V信号量可以像inteded一样工作,但这是特定于操作系统的。如果你有一个c++17库,我认为你总是希望使用条件变量而不是一些互斥变量,而你不能同时锁定这两个变量,如果你的库已经有了,你可以使用,如果不是的话,我肯定boost有办法equivalent@Mgetz这绝对是另一种有效的方法。您想让我将其添加到答案中吗?@Mgetz
scoped\u lock
在这里有什么帮助?全部想法