从两个线程访问计数器

时间:2015-12-03 08:47:04

标签: c++ multithreading

我有一个从一个线程递增的计数器。在主线程中,我基本上通过调用类的数据成员将其打印出来。在下面的代码中,没有打印出任何内容。

#include <iostream>
#include <thread>
#include <windows.h>
#include <mutex>

std::mutex mut;

class Foo
{
public:
    Foo(const int& m) : m_delay(m), m_count(0)
    {}

    void update()
    {
        std::cout << "count: " << this->m_count << std::endl;
    }

     void operator()()  
     {
        while (true){
            mut.lock();
            m_count++;
            mut.unlock();

            Sleep(m_delay);
        }
     } 

private:
    int m_delay;
    int m_count;
};

Foo *obj = new Foo(200);

int main() 
{
    std::thread *t = new std::thread(*obj);
    t->join();

    while(true)
    {
        obj->update();

        Sleep(10);
    }

    return 0;
}

1 个答案:

答案 0 :(得分:3)

原始代码的问题在于它复制了Foo对象:

std::thread *t = new std::thread(*obj);

这意味着副本会发生增量,因此原始Foo中的值永远不会发生变化,因此当main将其打印出来时(如果您移动了错误的join()} )值总是一样的。

解决方案是使用引用而不是副本:

std::thread *t = new std::thread(std::ref(*obj));

您还需要通过互斥锁保护变量的读取(或使用计数器的std::atomic<int>),以避免因同时读取和写入非原子变量而导致的未定义行为。

您还应该直接停止使用mut.lock()mut.unlock()use a scoped lock

还没有必要在堆上不必要地创建东西,过度使用new对于首先学习Java和C#的人来说是一个坏习惯。

您还可以通过使用标准C ++替换特定于Windows的Sleep调用来使代码可移植。

正确的版本是:

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

std::mutex mut;

class Foo
{
public:
    Foo(std::chrono::milliseconds m) : m_delay(m), m_count(0)
    {}

    void update()
    {
        int count = 0;
        {
            std::lock_guard<std::mutex> lock(mut);
            count = m_count;
        }
        std::cout << "count: " << count << std::endl;
    }

     void operator()()  
     {
        while (true)
        {
            {
                std::lock_guard<std::mutex> lock(mut);
                m_count++;
            }

            std::this_thread::sleep_for(m_delay);
        }
     } 

private:
    std::chrono::milliseconds m_delay;
    int m_count;
};

Foo obj(std::chrono::milliseconds(200));

int main() 
{
    std::thread t(std::ref(obj));
    while(true)
    {
        obj.update();

        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    t.join();
    return 0;
}

或者,使用原子变量,因此您不需要互斥锁:

#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

class Foo
{
public:
    Foo(std::chrono::milliseconds m) : m_delay(m), m_count(0)
    {}

    void update()
    {
        std::cout << "count: " << m_count << std::endl;
    }

     void operator()()  
     {
        while (true)
        {
            m_count++;

            std::this_thread::sleep_for(m_delay);
        }
     } 

private:
    std::chrono::milliseconds m_delay;
    std::atomic<int> m_count;
};

Foo obj(std::chrono::milliseconds(200));

int main() 
{
    std::thread t(std::ref(obj));
    while(true)
    {
        obj.update();

        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    t.join();
    return 0;
}