I am learning multithreading and have a simple program below to display some progress. I do not understand why the first code where the function is defined in either the .h file or in the main.cpp file works fine. But if I put the definition of the startLineThread in a new .cpp file like tools.cpp it seems that that second thread never gets a chance to write to the calcProgress variable despite it being and static. the console just loops with zero's and also ignores the calculating bool flag set to false, I can see while debugging after a while the loop in the second loop finishes and prints " done " to the console but the while loop in the main ignores the Tools::calculating == false and keeps looping.
------------------------- tools.h with definition within it ----------------------------
#pragma once
#include <atomic>
#include <iostream>
#include "tools.h"
#include <thread>
#include <chrono>
namespace Tools
{
static std::atomic<float> clacProgress;
static volatile bool cFinished;
static volatile bool calculating;
void startLineThread()
{
std::cout << " second thread active " << std::endl;
float dataPoints = 1000;
float dt = 0.5;
for (float t = 0.0f; t < dataPoints; t )
{
// bs code for making this thread slightly slower maybe
float no = 87 45;
int slow = 45 * 56 * 23;
std::this_thread::sleep_for(std::chrono::microseconds(8));
Tools::clacProgress = t;
}
//Tools::cFinished = true;
Tools::calculating = false;
std::cout << "done" << std::endl;
}
}
// this way works fine calcProgress is incremented and printed with updated value
// and the while loop in main exits when calculating becomes false;
-------------------------------main.cpp ----------------------------------------
#include <vector>
#include <string>
#include <stdio.h>
#include <math.h>
#include <chrono>
#include <sstream>
#include <thread>
#include "tools.h"
#include <atomic>
int main()
{
std::cout << " main thread active " << std::endl;
// initilize all statics before any other threads are active
//Tools::clacProgress = 0;
Tools::calculating = true;
Tools::cFinished = false;
std::thread testCalc(Tools::startLineThread);
while ( Tools::calculating)
{
std::cout << Tools::clacProgress << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(200));
}
std::cin.get();
}
------------------------------------------------------------------------------
Now If I put the definition of startLineThread into a tools.cpp file it breaks everything.
---------------------------- tools.h startLineThread declared but not defined -------
#pragma once
#include <atomic>
namespace Tools
{
static std::atomic<float> clacProgress;
static volatile bool cFinished;
static volatile bool calculating;
void startLineThread();
}
----------------------------- tools.cpp ------------
#include <iostream>
#include "tools.h"
#include <thread>
#include <chrono>
void Tools::startLineThread()
{
std::cout << " second thread active " << std::endl;
float dataPoints = 1000;
float dt = 0.5;
for (float t = 0.0f; t < dataPoints; t )
{
// bs code for making this thread slightly slower maybe
float no = 87 45;
int slow = 45 * 56 * 23;
std::this_thread::sleep_for(std::chrono::microseconds(8));
Tools::clacProgress = t;
}
//Tools::cFinished = true;
Tools::calculating = false;
std::cout << "done" << std::endl;
}
/* this way calcProgress is never incremented in the main thread and prints zero's despite the second thread kicking off and looping and I see " done " printed which indicates it did loop and the while loop in main never exits despite calculating becoming false */
-------------------------------main.cpp ----------------------------------------
#include <vector>
#include <string>
#include <stdio.h>
#include <math.h>
#include <chrono>
#include <sstream>
#include <thread>
#include "tools.h"
#include <atomic>
int main()
{
std::cout << " main thread active " << std::endl;
// initilize all statics before any other threads are active
//Tools::clacProgress = 0;
Tools::calculating = true;
Tools::cFinished = false;
std::thread testCalc(Tools::startLineThread);
while ( Tools::calculating)
{
std::cout << Tools::clacProgress << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(200));
}
std::cin.get();
}
------------------------------------------------------------------------------
And IF its a race condition I dont see why I have it in one circumstance and not the other? But I thought the static atomic would prevent the race condition for the threads updating/reporting the progress value
CodePudding user response:
These variables should not be defined in the header file:
static std::atomic<float> clacProgress;
static volatile bool cFinished;
static volatile bool calculating;
By defining them in the header you are making a separate copy of each variable in each .cpp file. Assuming you are using C 17 or newer, change static
to inline
. If you're using C 14 or older, change static
to extern
and then define the variables in the .cpp file (so there will be a single copy of each variable, see Declaring variables in header files C ).