关于 静态成员变量 和 全局变量 的深刻教训

为某高校做了一个视频播放ActiveX控件,基于opencv来做的视频采集,播放本地视频文件

最开始在控件的上做了播放按钮的界面和播放进度条等UI元素,所以用到了很多线程同步的机制, 

后来甲方让把UI元素都去掉,只保留视频窗口,于是修改了一顿,后来去掉了这些UI元素。

甲方的要求是要在自己的软件上嵌四个视频播放控件,我在做测试的时候也开了四个同时播放,但是,but,可是,可但是,这里是重点:我是在多个进程中嵌入多个控件,每个进程只有一个控件,没有在一个进程中嵌入多个控件,这里为后面的错误埋下了伏笔。

 

甲方在自己的测试中发现了问题,那就是同时调用播放函数Play()的时候只有一个视频会播放起来,其他三个都没有反应,而只要在每个Play()之间用Sleep延时一下就可以了,通过分析Play()的返回值,发现是返回成功,于是又观察任务管理器里这个进程的线程数量,发现如果一个控件成功播放了视频则对应增加9个线程,四个应该增加36个线程,但是当没有Sleep延时的时候只增加了9个线程,当使用了Sleep让四个都播放成功的时候增加了36个线程,这符合预期。  问题基本定位在【线程函数】里,由于某中原因线程函数由于竞争资源或其他什么原因导致只有一个线程成功。 同时在甲方那里的时候我用鼠标右键点击播放窗口,发现这个用来【暂停】的功能,会印象其他的视频窗口。

问题定位以后回到公司,单步,发现在调用opencv的cvCreateCapture函数时报断言失败,初步怀疑是这个函数不是线程安全的,对这个函数的调用增加了同步,发现四个视频控件可以同时开启视频播放,这个问题解决了。   然后用鼠标右键点击视频窗口去暂停的时候,发现还是有bug,然后基本发现了原因,问题就在于:线程函数是一个全局函数,控件类要和线程函数以及放在控件窗口上的CPlayer窗口类之间交换数据的时候用的都是【public static 变量】或者【全局变量】,而这样变量是进程内唯一的,因此这就导致了如果用某个这样的变量去让播放线程暂停的时候,其他线程也会知道这个变量的值改变了,所以也会受到影响,下一步的思路就是要解决这些变量,全部改为非静态成员变量,这就要看怎么和线程函数以及其他类交换数据。

 

通过创建线程的函数_beginthreadex下手,想把线程函数改为控件类的成员函数,但是又必须是static函数,这样static函数只能访问static变量和static成员函数,不符合要求。 那么把线程函数改为控件类的【友元函数】呢?  这样倒是可以,但是怎么交换数据呢,有了,就是在_beginthreadex函数中函数参数中传入this,如下:

class CMOVCtrl : public COleControl
{
    //codes
    
    friend unsigned int __stdcall Work(void* p); //线程函数
    //codes
}


HANDLE hThread = (HANDLE)_beginthreadex(
    NULL,
    0,
    WorkThread,  //线程函数
    this, //本类
    CREATE_SUSPEND, //根据实际需要来
    NULL    
);

unsigned int __stdcall WorkThread(void* p)
{
    //这里很重要,定义一个【引用】
    CMOVCtrl&  objMOV = *((CMOVCtrl*)(p));

   //使用objMOV来访问其变量
}  

 

 对于覆盖在控件类上的CPlayer类,它与控件类以及播放线程交换数据的时候也要把该类中的变量把static变为普通的变量,有个地方是在CPlayer中要使用控件类的窗口句柄,

而在控件类中又要调用CPlayer类的Create(UINT 模版, CWnd* pParent)方法来创建,这样可以在CPlayer中增加一个函数和一个HWND类型成员,然后在控件类中如下:

 

//MOVCtrl.cpp

//codes

m_dlgVideo.InitCPlayer(m_hWnd,  IDD_DLG, this); 即可, m_dlgVideo为一个CPlayer类型的成员变量。

 

好久没写博客了,最近也没怎么好好看书。

 

以上

 

时间:2013年12月7日 16:12:41  

   

 

 

 

posted on 2013-12-07 16:13  崔好好  阅读(2086)  评论(0编辑  收藏  举报

导航