多线程程序仅适用于print语句

use*_*454 1 c++ multithreading race-condition c++11

我希望我能想到一个更具描述性的标题,但事实就是这样.我有一些代码,我想用它做一些图像处理.我还需要从那些处理过的图像中获取一些统计数据,我想在一个单独的线程上做这个,所以我的主线程可以继续进行图像处理.

除此之外,这是我的代码.除了我的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();
}
Run Code Online (Sandbox Code Playgroud)

我认为来自cout的延迟是避免一些竞争条件我否则会遇到,但是什么?因为只有一个线程修改了bool goodToGo,而另一个线程检查了它,应该是一个线程安全的"门控"两个函数的方法吗?

对不起,如果有什么不清楚的话,我对此很新,并且似乎犯了很多明显的错误WRT多线程编程.

谢谢你的帮助

  • 约翰

Mat*_*son 5

当你有:

 while (cfd.goodToGo == false){  }
Run Code Online (Sandbox Code Playgroud)

编译器没有看到任何理由"重新加载"它的值goodToGo(它不知道这个值受其他线程的影响!).所以它读取一次,然后永远循环.

打印的东西有所不同的原因是,编译器不知道打印函数实际上将会影响什么,所以"以防万一",它重新加载该值(如果编译器可以"看到内部"全部打印代码,它实际上可以决定goodToGo打印不会改变,而不需要重新加载 - 但是有多少时间[或某些代理时间限制,例如"呼叫级别数"或"数量"中间指令""编译器花费在计算这些事情上[当然可能会调用代码,编译器实际上无法访问源代码,例如系统调用write或类似).

然而,解决方案是使用线程安全机制来更新goodToGo- 我们可以volatile向变量抛出一个属性,但这并不能保证,例如,另一个处理器被"告知"该值已更新,因此可以延迟检测更新的值[甚至在某些条件下甚至无限].

使用std::atomic_bool goodToGostoreload函数来访问里面的值.这样,您将保证正确更新该值,并且"立即"更新(如下几十到几百个时钟周期)另一个线程看到的值.

作为旁注,这可能应该是真正的答案:忙碌等待线程通常是一个坏主意,你应该看一些线程原语来等待condition_variable或类似.