这个简单的 Mutex 没有任何作用

This simple Mutex does not have any effect

我正在尝试学习多线程的基础知识。下面的示例来自“多处理器编程艺术”一书,其中有(Java):

class Counter
{
    private long value;
    private Lock lock;

    public long getAndIncrement()
    {
        lock.lock();
        try
        {
            long temp = value;
            value = temp + 1;
            return temp;
        }
        finally
        {
            lock.unlock();            
        }
    }
}

java 代码结束。书中说上面显示的“lock() 和 unlock()”将为这个共享计数器实现添加互斥。 (我无法 运行 这个 java 自己编写代码,而且我认为应该返回“value”而不是“temp”。)

当我尝试这个简单的示例(在我可以尝试的 C++ 中)时,我的互斥体没有做任何事情。我可以删除它,结果是一样的。 C++代码:

#include <iostream>
#include <thread>
#include <mutex>

class Counter
{
public:
    Counter(int v)
    : value(v)
    {}

    int getAndIncrement()
    {
        std::lock_guard<std::mutex> lk(m);
        return value++;
    }

    void show() const
    {
        std::cout << "value = " << value << '\n';
    }

private:
    int value;
    std::mutex m;
};

int main()
{
    Counter c1(1);

    std::thread t1(&Counter::getAndIncrement, &c1);
    t1.detach();
    c1.show(); // 1 or 2

    std::thread t2(&Counter::getAndIncrement, &c1);
    t2.detach();
    c1.show(); // 1 or 2

    //int n = c1.getAndIncrement();
    //std::cout << n << '\n'; // 1 or 2 or 3. never 4

    c1.getAndIncrement();
    c1.show(); //2, 3 and sometimes 4
}

注释显示我 运行 代码时的输出。我的互斥体没有效果。 任何指南将不胜感激。 谢谢。

互斥量的作用应该是确保没有两个线程同时读取或写入成员(只读就可以)。在您的代码中情况并非如此,因为同时一个线程锁定了互斥锁并写入主线程可能调用的成员 show。您需要保护任何访问权限:

void show() const
{
    std::lock_guard<std::mutex> lk(m);
    std::cout << "value = " << value << '\n';
}

那么你的代码是好的,你观察到的输出是预期的。

您正在启动 2 个线程,您无法知道这些线程是先从成员写入还是先从成员读取。

可能的顺序是:

  thread t1 writes
  main calls show          2
  main calls show          2
  main increments
  main calls show          3
  thread t2 writes       

另一个可能的顺序是:

  main calls show          1
  main calls show          1
  thread t2 writes         
  thread t1 writes
  main increments
  main calls show          4

互斥锁只保护对成员的并发访问。您可以确定这将按顺序发生:

  main calls show
  main calls show
  main increments
  main calls show

如果您希望事情在不同线程之间也以特定顺序发生,您需要添加某种同步(例如,条件变量)。尽管对于您的简单示例,这基本上对应于没有并行性,并且添加线程只会增加开销。


So final value of "value" is not supposed to be 4

让我们从您的代码中删除 detach 并添加一些 join

int main()
{
    Counter c1(1);

    std::thread t1(&Counter::getAndIncrement, &c1);
    c1.show(); // 1 or 2

    std::thread t2(&Counter::getAndIncrement, &c1);
    c1.show(); // 1 or 2 or 3

    c1.getAndIncrement();
    c1.show(); // 2, 3 or 4
    t1.join();
    t2.join();
    c1.show(); // definitely 4 !!!
}

join 等待线程完成。即:我们可以确定到 join returns 时,线程已经从 getAndIncrement 返回。最后的 show 肯定会打印 4。在一个更简单的例子中:

int main()
{
    Counter c1(1);

    std::thread t1(&Counter::getAndIncrement, &c1);
    c1.show(); // 1 or 2

    t1.join();
    c1.show(); // 2
}

PS:我还不能完全确定你的误会到底是什么is/was。考虑到多线程不仅仅是单线程+线程。它是设计的一部分。通常会使用不同的算法或数据结构 must/should。