C++ 线程没有看到带锁的刷新值
如何强制线程查看共享变量的更新值?我认为互斥锁应该有帮助,但在下面的代码中,我看不到正确的行为:C++ 线程没有看到带锁的刷新值,c++,multithreading,C++,Multithreading,如何强制线程查看共享变量的更新值?我认为互斥锁应该有帮助,但在下面的代码中,我看不到正确的行为: #include <thread> #include <vector> #include <chrono> #include <iostream> #include <mutex> using namespace std; mutex coutm; class Timer { public: Timer() :
#include <thread>
#include <vector>
#include <chrono>
#include <iostream>
#include <mutex>
using namespace std;
mutex coutm;
class Timer {
public:
Timer() :
m_start(chrono::high_resolution_clock::now()),
m_elapsedTime (0) {}
~Timer() {};
void start() {
while (m_elapsedTime < 50) {
unique_lock<mutex> timerLock{m_timerMutex};
m_elapsedTime += chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - m_start).count();
unique_lock<mutex> lck{coutm};
cout << "Timer thread. New timer value = " << m_elapsedTime << endl;
this_thread::yield();
}
unique_lock<mutex> lck{coutm};
cout << "Time = " << m_elapsedTime << ". Done!" << endl;
}
double getElapsedTime() {
return m_elapsedTime;
}
private:
double m_elapsedTime;
decltype(chrono::high_resolution_clock::now()) m_start;
public:
mutex m_timerMutex;
};
void dummyPrint(Timer& m) {
unique_lock<mutex> timerLock{m.m_timerMutex};
auto t = m.getElapsedTime();
while (t < 50) {
unique_lock<mutex> lck{coutm};
cout << "Waiting... Time = " << t << endl;
this_thread::yield();
}
}
int main() {
Timer t;
thread timerThread {&Timer::start, std::ref(t)};
thread t1{&dummyPrint, std::ref(t)};
timerThread.join();
t1.join();
return 0;
}
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
互斥coutm;
班级计时器{
公众:
计时器():
m_start(时钟::高分辨率时钟::现在()),
m_elapsedTime(0){}
~Timer(){};
void start(){
而(m_elapsedTime<50){
唯一的_locktimerlock{m_timerMutex};
m_elapsedTime+=chrono::duration_cast(chrono::high_resolution_clock::now()-m_start).count();
唯一锁lck{coutm};
库特
仅仅因为您首先构建了timerThread
,然后构建了t1
,您无法保证Timer::start
将首先在其执行线程中运行,而dummyPrint
将第二次在其自己的执行线程中运行。它不仅必须首先开始执行,还必须锁定首先是互斥锁,以便您看到预期的结果。这也不能保证。这里唯一的保证是新的执行线程将在std::thread
完全构造后的某个点进入线程函数。就是这样
无论出于什么原因,在您的特定C++实现中,<代码> DimyPrime>代码>执行线程首先将其动作组合在一起,锁定互斥体,并发现它尚未初始化,并且<代码> MyELAPSEDSETIME> <代码>仍然是0。这就是您所看到的行为。
当涉及到多个执行线程时,您几乎没有什么保证。如果您需要特定的行为,您需要自己强制执行。在这里,最简单的操作过程是添加一个单独的
bool
标志,以及一个指示timerThread
完成初始化的条件变量。timerThread
初始化所有内容,设置标志,并向条件变量发送信号。main
在构建第二个std::thread
之前,等待设置标志
注意:不要忽略条件变量。收益率
不如正确使用条件变量可靠
仅仅因为您首先构建了timerThread
,然后构建了t1
,您无法保证Timer::start
将首先在其执行线程中运行,而dummyPrint
将第二次在其自己的执行线程中运行。它不仅必须首先开始执行,还必须锁定首先是互斥锁,以便您看到预期的结果。这也不能保证。这里唯一的保证是新的执行线程将在std::thread
完全构造后的某个点进入线程函数。就是这样
无论出于什么原因,在您的特定C++实现中,<代码> DimyPrime>代码>执行线程首先将其动作组合在一起,锁定互斥体,并发现它尚未初始化,并且<代码> MyELAPSEDSETIME> <代码>仍然是0。这就是您所看到的行为。
当涉及到多个执行线程时,您几乎没有什么保证。如果您需要特定的行为,您需要自己强制执行。在这里,最简单的操作过程是添加一个单独的
bool
标志,以及一个指示timerThread
完成初始化的条件变量。timerThread
初始化所有内容,设置标志,并向条件变量发送信号。main
在构建第二个std::thread
之前,等待设置标志
注意,不要吝啬条件变量。yield
不如正确使用条件变量可靠。你的意思是在循环内刷新t
的值吗?愚蠢的我!是的。那应该在循环内!谢谢。Timer::start
没有保护它在lo中访问m\u elapsedTime
操作条件或最终打印,并且dummyPrint
直到函数结束才会释放锁。您可以使用std::atomic m_elapsedTime;
来防止UB将其更改为这样:您的意思是刷新循环中t
的值吗?我真蠢!是的。这应该在循环中!谢谢。计时器::start
在循环条件或最终打印中不保护其对m_elapsedTime
的访问,并且dummyPrint
在函数结束前不会释放锁。您可以使用std::atomic m_elapsedTime;
来防止它这样更改:
thread timerThread {&Timer::start, std::ref(t)};
thread t1{&dummyPrint, std::ref(t)};