我希望我能想到一个更具描述性的标题,但事实就是如此。我有一些代码,我想用它做一些图像处理。我还需要从那些处理过的图像中获取一些统计数据,我想在一个单独的线程上做这个,所以我的主线程可以继续进行图像处理。
除此之外,这是我的代码。除了我的Image类包装OpenCV Mat这一事实之外,它应该不是真正相关的(尽管据我所知,我没有使用OMP或任何东西):
#include <thread>
#include <iostream>
#include <vector>
using namespace std;
//Data struct
struct CenterFindData{
//Some images I'd like to store
Image in, bpass, bpass_thresh, local_max, tmp;
//Some Data (Particle Data = float[8])
vector<ParticleData> data;
//My thread flag
bool goodToGo{ false };
//Constructor
CenterFindData(const Image& m);
};
vector<ParticleData> statistics(CenterFindData& CFD);
void operate(vector<CenterFindData> v_CFD);
..........................................
..........................................
..........................................
void operate(vector<CenterFindData> v_CFD){
//Thread function, gathers statistics on processed images
thread T( [&](){
int nProcessed(0);
for (auto& cfd : v_CFD){
//Chill while the images are still being processed
while (cfd.goodToGo == false){
//This works if I uncomment this print statement
/*cout << "Waiting" << endl;*/
}
cout << "Statistics gathered from " << nProcessed++ << " images" << endl;
//This returns vector<ParticleData>
cfd.data = m_Statistics(cfd);
}
});
//Run some filters on the images before statistics
int nProcessed(0);
for (auto& cfd : v_CFD){
//Preprocess images
RecenterImage(cfd.in);
m_BandPass(cfd);
m_LocalMax(cfd);
RecenterImage(cfd.bpass_thresh);
//Tell thread to do statistics, on to the next
cfd.goodToGo = true;
cout << "Ran filters on " << nProcessed++ << " images" << endl;
}
//Join thread
T.join();
}
我认为来自cout的延迟是为了避免一些竞争条件我否则会遇到,但是什么?因为只有一个线程修改了bool goodToGo,而另一个线程检查了它,是否应该是一种“选通”两个函数的线程安全方式?
很抱歉,如果有什么不清楚的话,我对此很新,并且似乎在WRT多线程编程中犯了很多明显的错误。
感谢您的帮助
答案 0 :(得分:5)
当你有:
while (cfd.goodToGo == false){ }
编译器没有看到任何重新加载&#34;的原因。 goodToGo
的值(它不知道此值受其他线程的影响!)。所以它读取一次,然后永远循环。
印刷品有所不同的原因是,编译器不知道印刷功能实际上会是什么并且不会影响,所以&#34;以防万一&#34;,它重新加载值(如果编译器可以&#34;看到内部&#34;所有打印代码,它实际上可能决定goodToGo
不会被打印更改,并且不需要重新加载 - 但是有限制多长时间[或某些时间的代理,例如&#34;呼叫等级的数量&#34;或者#34;中间指令的数量&#34;]编译器花费在计算这些事情上[和那里当然可以调用代码,编译器实际上无法访问源代码,例如系统调用write
或类似的代码。
然而,解决方案是使用线程安全机制来更新goodToGo
- 我们可以向变量抛出volatile
属性,但这并不能保证,例如,另一个处理器得到&#34;告诉&#34;该值已更新,因此可能会显着延迟更新值的检测[甚至在某些条件下无限延迟]。
使用std::atomic_bool goodToGo
以及store
和load
函数访问其中的值。这样,您将确保正确更新该值并立即&#34;立即&#34; (如同之前的几十到几百个时钟周期)另一个线程看到了。
作为旁注,这可能应该是真正的答案:忙碌等待线程一般来说是一个坏主意,你应该看一些线程基元等待condition_variable
或类似。