MFC框架下自定义类中定义多线程问题

本人尝试在MFC框架下自定义一个类,该类负责接收和解码视频数据,因此需要在该类中定义多线程来做这件事情,以防止在对话框调用该类的时候卡死。
目前本人在自定义类中使用了static的方式开启了多线程,在win10下运行也很正常。然而在测试兼容性的时候发现该程序在win7 64位下会出错,出错的原因似乎是因为我在类内使用了static函数定义了多线程,而我又声明了该类的几个实体,因此static函数出现了调用实体出错的问题。
想问问看:

1.有没有老哥遇到过这种问题?

2.有没有比较好的解决思路?

3.是不是不应该在一个类中用static函数的形式定义线程?

4.像多路接收解码的功能除了封装成一个类然后使用多个实体来做外,还有没有更好的封装方式?

class HWdecode
{
public:
    bool changertspurl = false;//是否改变rtsp的地址标志
    bool reconnect = false;//需要断线重连标志
    bool haveconnect = false;//正常连接标志位
    CCriticalSection* mlock;//线程临界区
    bool dodecodesave = false;//像素转换开启标志位
    //参数对象
    FFmpegDemuxer* demuxer = NULL;//FFMPEG对象
    CUcontext cuContext;//Cuda设备
    NvDecoder* dec = NULL;//Cuda解码对象
    CUdeviceptr dpFrame = 0;//数据存储对象初始化
    int hwinit()//显卡设备初始化
    {
        int iGpu = 0;//选择解码播放的GPU,只有一个的话直接设置为0就行
        //检测硬件GPU设备
        ck(cuInit(0));
        int nGpu = 0;
        ck(cuDeviceGetCount(&nGpu));
        if (iGpu < 0 || iGpu >= nGpu)
        {
            std::cout << "没有相应的GPU" << std::endl;
            return 0;
        }
        //获得CUDA对象
        CUdevice cuDevice = 0;
        ck(cuDeviceGet(&cuDevice, iGpu));
        char szDeviceName[80];
        ck(cuDeviceGetName(szDeviceName, sizeof(szDeviceName), cuDevice));
        std::cout << "GPU in use: " << szDeviceName << std::endl;
        ck(cuCtxCreate(&cuContext, CU_CTX_SCHED_BLOCKING_SYNC, cuDevice));
        return 1;
    }
    int ffmpeginit(char* url)//ffmpeg对象初始化
    {
        demuxer = new FFmpegDemuxer(url);
        if (demuxer->fmtc == NULL)
        {
            return 0;
        }
        else
        {
            //RGBA帧存储显存初始化

            return 1;
        }
    }
    void cudadecoderinit()//cuda解码对象初始化
    {
        ck(cuMemAlloc(&dpFrame, demuxer->GetWidth() * demuxer->GetHeight() * 4));
        //解码器初始化
        dec = new NvDecoder(cuContext, (*demuxer).GetWidth(), (*demuxer).GetHeight(), true, FFmpeg2NvCodecId((*demuxer).GetVideoCodec()));
    }
    DWORD decAndshow()//解码和播放
    {
        int nVideoBytes = 0, nFrameReturned = 0, nFrame = 0;
        uint8_t* pVideo = NULL, ** ppFrame;
        long bt = clock();

        do
        {
            uint8_t* getdpframe = (uint8_t*)dpFrame;
            demuxer->Demux(&pVideo, &nVideoBytes);//获取一帧视频数据
            dec->Decode(pVideo, nVideoBytes, &ppFrame, &nFrameReturned);//解码这一帧数据
            //if (!nFrame && nFrameReturned)
            //  LOG(INFO) << HWD.dec->GetVideoInfo();
            if (dodecodesave)
            {
                for (int i = 0; i < nFrameReturned; i++)
                {
                    mlock->Lock();
                    //对解码出来的图像数据类型做转换,转到BGRA
                    printf("GetWidth %d\n", dec->GetWidth());
                    printf("GetHeight %d\n", dec->GetHeight());
                    if (dec->GetBitDepth() == 8)
                    {
                        if (dec->GetOutputFormat() == cudaVideoSurfaceFormat_YUV444)
                            YUV444ToColor32<RGBA32>((uint8_t*)ppFrame[i], dec->GetWidth(), getdpframe, 4 * dec->GetWidth(), dec->GetWidth(), dec->GetHeight());
                        else    // default assumed as NV12
                            Nv12ToColor32<RGBA32>((uint8_t*)ppFrame[i], dec->GetWidth(), getdpframe, 4 * dec->GetWidth(), dec->GetWidth(), dec->GetHeight());
                    }
                    mlock->Unlock();
                }
            }
            nFrame += nFrameReturned;
        } while (nVideoBytes && changertspurl == false);
        changertspurl = false;//将rtsp地址修改标志位重新设置为否
        printf("退出,尝试断线连回\n");
        reconnect = true;
        delete demuxer;
        std::cout << "Total frame decoded: " << nFrame << std::endl;
        std::cout << "花费时间:" << clock() - bt << "ms" << std::endl;
        std::cout << "FPS:" << nFrame / ((clock() - bt) / 1000.0) << std::endl;
        return 1;
    }
    HANDLE pRecvAndShowThread;//接收显示线程对象
    static DWORD ThreadRtspRecvShowFun(LPVOID lpParam)
    {
        return ((HWdecode*)lpParam)->decAndshow();//调用线程的处理函数
    }
    int Star(char* url, CCriticalSection * lock)
    {
        mlock = lock;
        if (!hwinit())//初始化硬件设备
        {
            std::cout << "硬件初始化失败!" << std::endl;
            return 0;
        }
        if (!ffmpeginit(url))//初始化硬件设备
        {
            std::cout << "无法连接码流!" << std::endl;
            return 0;
        }
        cudadecoderinit();
        pRecvAndShowThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HWdecode::ThreadRtspRecvShowFun, this, 0, NULL);
        return 1;
    }
    int ReStar(char* url, CCriticalSection* lock)//断线重连
    {

        if (!ffmpeginit(url))//初始化硬件设备
        {
            std::cout << "无法连接码流!" << std::endl;
            return 0;
        }
        if (!haveconnect)
        {
            cudadecoderinit();
        }
        pRecvAndShowThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HWdecode::ThreadRtspRecvShowFun, this, 0, NULL);
        return 1;
    }
};
c++

3个回答

我看了你的描述,你应该好好看看这几个类的数据是怎么做的,为什么会串,你要是可以贴一下你类的代码,更好

phenix2009
白色一大坨 回复_寒潭雁影: 我测试方法是ffmpeginit中把url拷贝到我的一个成员变量字符串里面,然后在decAndshow中打印了这个成员变量,如果线程有问题,那么肯定会混,但实际上,我七个不同的输入,打印的也都是其对应数组,我初步判断,你线程这块没有问题
3 个月之前 回复
phenix2009
白色一大坨 回复_寒潭雁影: 我拷贝你代码,去掉了库函数,自己弄了个类成员,拷贝不同url进去,打印了一下都是各自类输入的,不会混,你们我建议你查一下这个demuxer的初始化是怎么混的,反正线程应该是没问题的
3 个月之前 回复
oHanTanYanYing
_寒潭雁影 回复白色一大坨: mhwdecoder[i].Star(url, &datalock[i]);调用代码就是在外面示例化7个对象,然后全部调用一次star函数
3 个月之前 回复
phenix2009
白色一大坨 回复_寒潭雁影: 调用代码也发一下吧
3 个月之前 回复
phenix2009
白色一大坨 回复_寒潭雁影: static DWORD ThreadRtspRecvShowFun(LPVOID lpParam) { return ((HWdecode*)lpParam)->decAndshow();//调用线程的处理函数 }我觉得这个是问题,你确定调用线程时候,输入的这个参数,是不同类吗,如果出问题,应该从这个参数来分析吧
3 个月之前 回复
oHanTanYanYing
_寒潭雁影 您好,我已经更新了类代码,如果您有时间麻烦帮忙看看。我的数据全部是类内定义的,感觉应该还是因为非静态成员属于实体而静态成员数据属于类导致的该问题
3 个月之前 回复

按照你的描述,只有一个工作线程和一个主线程,是这样么?
首先检查是不是会出现多个工作线程并发,如果那样的话,应该做同步。或者说你编码的代码是线程不安全的,需要修改。
其次检查你的UI线程和工作线程的通讯代码,工作线程不能直接操作UI,而是应该同步让主线程访问。
static函数的形式定义线程,这个应该没问题。

oHanTanYanYing
_寒潭雁影 回复贵阳老马马善福专业维修游泳池堵漏防水工程: 但是我看static方法如果调用非静态变量,它每次都是会创建自己的一个内存的,也就不存在线程冲突的问题,我在Win10上这么写是没问题的,但在win7上就产生了错乱(分别测试了3、4台机器都是这样),我想应该还是线程设计缺陷问题
3 个月之前 回复
oHanTanYanYing
_寒潭雁影 回复贵阳老马马善福专业维修游泳池堵漏防水工程: 回复贵阳老马马善福专业维修游泳池堵漏防水工程: 是的,我设计的是一个类实体就对应一个rtsp的接收和解码,那么我想接收n个视频就会声明n个实体,这样也就会开启n条线程
3 个月之前 回复
caozhy
每个人都有一个梦才不会孤单的说话就有天堂 回复_寒潭雁影: pRecvAndShowThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HWdecode::ThreadRtspRecvShowFun, this, 0, NULL); 这里调试下,是不是存在创建多个线程的情况,你的代码还调用了 ffmpeg cuda,这些都是线程不安全的
3 个月之前 回复
oHanTanYanYing
_寒潭雁影 您好,我已经上传了类的代码,可能看起来会清楚一点。您说的同步应该怎么做呢?
3 个月之前 回复
caozhy
每个人都有一个梦才不会孤单的说话就有天堂 回复白色一大坨: 我不能确定他到底怎么做的,他的描述很含糊,静态线程是静态变量里面定义的线程还是用某个静态函数启动的线程,启动了几个线程。这些都不确定,但是从他的描述看,应该就是线程同步没有做的可能性大
3 个月之前 回复
caozhy
每个人都有一个梦才不会孤单的说话就有天堂 否则静态函数会重入,静态变量会出现脏读。不做同步处理的代码,显然不能正确运行
3 个月之前 回复
phenix2009
白色一大坨 回复贵阳老马马善福专业维修游泳池堵漏防水工程: 只有线程是静态的,不可能混吧
3 个月之前 回复
caozhy
每个人都有一个梦才不会孤单的说话就有天堂 回复_寒潭雁影: 你有n个类,并且同时访问了静态函数或者静态变量,是这样么?那么你需要同步
3 个月之前 回复
phenix2009
白色一大坨 回复_寒潭雁影: 不同类之间怎么可能有关系
3 个月之前 回复
oHanTanYanYing
_寒潭雁影 你好,我是在一个类里面开启了接收解码线程,然后在主线程的时候根据需要实例化N个这个类的实体,开启它们类里面的线程。出现的问题就是在win7 64位中这些线程似乎存在相互冲突的情况,也就是说实体1可能会访问实体2的数据导致出错。在win10中不存在这个问题,不知道是不是系统底层多线程调度产生的冲突导致的
3 个月之前 回复

错误信息帖出来,系统日志里,报个什么错截图,单猜测可能不准。还有不可以在win7电脑上直接源码调试吗

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问