C++ 这个简单的互斥锁没有任何效果

C++ 这个简单的互斥锁没有任何效果,c++,mutex,C++,Mutex,我正在努力学习多线程的基础知识。以下示例摘自《多处理器编程的艺术》(Java)一书: java代码的结尾。书中说,上面显示的“lock()和unlock()”将为这个共享计数器实现添加互斥。(我自己无法运行这个java代码,并且认为应该返回“value”而不是“temp”。) 当我尝试这个简单的例子(C++中我可以尝试)我的互斥体什么也不做。我可以删除它,结果将是一样的。 C++代码: #include <iostream> #include <thread> #incl

我正在努力学习多线程的基础知识。以下示例摘自《多处理器编程的艺术》(Java)一书:

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
}
#包括
#包括
#包括
班级计数器
{
公众:
计数器(INTV)
:值(v)
{}
int getAndIncrement()
{
标准:锁紧装置lk(m);
返回值++;
}
void show()常量
{

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

void show() const
{
    std::lock_guard<std::mutex> lk(m);
    std::cout << "value = " << value << '\n';
}
另一种可能的顺序是:

  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
如果你想在不同的线程之间以特定的顺序发生事情,你需要添加某种同步(例如,一个条件变量)。尽管对于你的简单示例,基本上没有并行性,添加线程只会增加开销


所以“value”的最终值不应该是4

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

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
返回时,线程已经从
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:我还不能完全确定你的误解到底是什么。考虑多线程不仅仅是单线程+线程。它是设计的一部分。通常必须使用不同的算法或数据结构。

你期待什么输出?为什么?我不理解你希望观察到的互斥体的影响。这段代码我希望是4。因为我认为进入“getAndIncrement()”的每个线程都应该首先锁定(并阻止其他线程同时进入),执行一个++操作,然后解锁它,以便下一个线程可以执行同样的操作。线程需要时间来启动。很可能在线程启动之前调用
show
。@mhm不能保证这些线程将以任何特定的顺序获得互斥锁。互斥锁保证不会有两个线程执行
value++
并同时或重叠计算它们的返回值。看起来它工作得很好。您希望不会发生什么影响?根据您的评论,您希望
main()
线程最后读取值(在所有增量完成后)。但是您没有采取任何措施阻止主线程向前运行,并在其他线程执行任何操作之前打印值。线程可以在一段时间后增加计数器。此代码很可能每次都打印
0
。因此“value”的最终值不应该是4?因为调用了3x++操作。@mhm最终值是4,是的。在这种情况下,请参阅editI建议将io保留在受保护区域之外:
void show()const{int valueLocal;{std::lock\u guard lk(m);valueLocal=value;}std::cout final是4,即使使用分离而不是加入?@fabian why?@mhm您希望在关键部分尽可能少地执行操作,以将其他线程等待释放锁的时间保持在最低限度。复制字段值很快,将其写入stdout也没有那么快,因此在写入之前销毁
锁保护
ng是最好的。
int main()
{
    Counter c1(1);

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

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