一个互斥锁与多个互斥锁。线程池用哪个好?

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

这里的例子,只是想保护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();

方法二,只使用一个互斥量:

    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. 我想确保方法一(多互斥锁)确保只有一个线程可以同时访问iData。
  2. 如果方法 1 正确,不确定方法 1 是否优于方法 2? 谢谢!
  1. I want to make sure that the Method 1 (multiple mutexes) ensures that only one thread can visit the iData at the same time.

您的第一个示例在堆栈上创建了一个本地互斥变量,它不会与其他线程共享。所以完全没有用。
它不能保证对 iData.

的独占访问
  1. If Method 1 is correct, not sure Method 1 is better of Method 2?

不正确。

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

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

方法 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 仍然更好,即使锁和数据之间的内聚性较弱。

其他答案在技术层面上是正确的,但是缺少一个重要的与语言无关的东西:你总是喜欢尽量减少不同mutexes/locks/ ... !

因为:一旦你有超过 一个 线程需要获取的东西才能做某事(然后释放所有获取的锁) order 变得至关重要。

当你有 两个 锁时,你必须使用不同的代码片段,例如:

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

getLockB() {
  getLockA() { 

您可以很快 运行 进入死锁 - 因为两个 threads/processes 可以每个获得一个锁 - 然后它们都被卡住,等待另一个释放它的锁。当然 - 在查看上面的示例时 "you would never make a mistake, and always go A first then B"。但是,如果这些锁存在于应用程序的完全不同部分怎么办?而且它们不是通过相同的方法或 class 获取的,而是在 3、5 次嵌套方法调用过程中获取的?

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