一个互斥锁与多个互斥锁.哪一个更适合线程池?

One mutex vs Multiple mutexes. Which one is better for the thread pool?

本文关键字:线程 一个 哪一个      更新时间:2023-10-16

这里的例子,只是想保护iData以确保只有一个线程同时访问它。

struct myData;
myData iData;

方法一,调用函数内部互斥锁(可以创建多个互斥锁):

void _proceedTest(myData &data)
{
std::mutex mtx;
std::unique_lock<std::mutex> lk(mtx);
modifyData(data);
lk.unlock;
}
int const nMaxThreads = std::thread::hardware_concurrency();
vector<std::thread> threads;
for (int iThread = 0; iThread < nMaxThreads; ++iThread)
{
threads.push_back(std::thread(_proceedTest, iData));
}
for (auto& th : threads) th.join();

方法2,只使用一个互斥锁:

void _proceedTest(myData &data, std::mutex &mtx)
{
std::unique_lock<std::mutex> lk(mtx);
modifyData(data);
lk.unlock;
}
std::mutex mtx;
int const nMaxThreads = std::thread::hardware_concurrency();
vector<std::thread> threads;
for (int iThread = 0; iThread < nMaxThreads; ++iThread)
{
threads.push_back(std::thread(_proceedTest, iData, mtx));
}
for (auto& th : threads) th.join();
  1. 我想确保方法 1(多个互斥锁)确保只有一个线程可以同时访问 iData。
  2. 如果方法 1
  3. 正确,不确定方法 1 是否比方法 2 更好? 谢谢!
我想
  1. 确保方法 1(多个互斥锁)确保只有一个线程可以同时访问 iData。

您的第一个示例在堆栈上创建一个局部互斥变量,它不会与其他线程共享。因此,它完全没用。
它不会保证独占访问iData

    如果方法 1
  1. 正确,不确定方法 1 比方法 2 更好?

这是不正确的。

其他答案在技术层面上是正确的,但缺少一个重要的语言独立的东西:你总是喜欢尽量减少不同互斥锁/锁/...!

因为:一旦线程需要获取多个东西才能做某事(然后释放所有获取的锁),顺序就变得至关重要。

当你有两个锁,并且你必须有不同的代码段,比如:

getLockA() {
getLockB() { 
do something
release B
release A

getLockB() {
getLockA() { 

您可能会很快遇到死锁 - 因为两个线程/进程可以各自获取一个锁 - 然后它们都被卡住,等待另一个线程释放其锁。当然 - 当查看上面的例子时,"你永远不会犯错误,总是先走 A 然后 B"。但是,如果这些锁存在于应用程序的完全不同的部分怎么办?它们不是在相同的方法或类中获取的,而是在 3、5 次嵌套方法调用的过程中获得的?

因此:当您可以使用一把锁解决您的问题时 - 仅使用把锁!完成某事所需的锁越多,最终陷入死锁的风险就越高。

方法 1 仅在将互斥变量设为static时才有效。

void _proceedTest(myData &data)
{
static std::mutex mtx;
std::unique_lock<std::mutex> lk(mtx);
modifyData(data);
lk.unlock;
}

这将使mtx由所有进入_proceedTest的线程共享。

由于static函数范围变量仅对函数的用户可见,因此对于传入的data来说,它实际上并不是一个足够的锁。这是因为可以想象,多个线程可以调用每个线程想要操作的不同函数data

因此,即使方法 1 是可以挽救的,方法 2 仍然更好,即使锁和数据之间的内聚力很弱。

一旦您离开_proceedTest范围,版本 1 中的互斥锁将超出范围,像这样锁定互斥锁是没有意义的,因为它永远不会被其他线程访问。

在第二个版本中,多个线程可以共享互斥锁(只要它不超出范围,例如作为类成员),这样一个线程可以锁定它,另一个线程可以看到它被锁定(并且也无法锁定它,因此称为互斥)。