我有一个从一个线程递增的计数器。在主线程中,我基本上通过调用类的数据成员将其打印出来。在下面的代码中,没有打印出任何内容。
#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;
}
答案 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;
}