回调函数,你拿完数据咋不跑嘞?

来源:互联网 发布:手机聚合软件 编辑:程序博客网 时间:2024/05/18 00:47


回调函数是一个很好的机制,使我们在需要数据不需要主动去获取,只要数据传输过来,程序可以自动地处理。
回调函数要尽快、及时退出。这是所有地方,无论是博客还是相关书籍,都是如此强调的。但是当自己写出一个回调函数,它真的能及时退出吗?下面将以一个MFC程序为例探讨一二。

  1. MFC程序概况

这个MFC程序是一个IPC Demo。在能够正常预览视频的条件下,获取码流信息并显示在一个对话框上。在SDK中,已定义好码流回调函数原型typedef void (STDCALL *pCallBackFunction)(int param1, type1 param1, ...),它会携带我们需要的码流信息;以及回调函数注册函数void setCallBackFunction(type1 param1, pCallBackFunction pUserDefinedCBFunc, type2, param, ...),它会把我们自己定义的处理数据的回调函数注册进去。

  1. 出现的问题——程序在点击了一个按钮后卡死了

我的回调函数如下所示:

void STDCALL myCallBackFunc(int param1, type2 param2, ...){    CString str;    str,format("--- the buffer length is [%l] ---", param1);    m_edit.setWindowText(str); // m_edit is an edit control to display callback information}

界面上有两个按钮,一个是开始获取回调函数,点击后将执行setCallBackFunction(…),同时开始获取码流信息;另一个是停止获取回调函数,点击后同样将执行setCallBackFunction(…),只不过此时把函数指针实参设为NULL。

到目前为止,一切都还很正常。没错,然后我就开始编译、运行,开启一路视频的预览,然后点击显示码流信息的按钮,编辑框内就刷刷的不停显示数据。然后当我点击关闭按钮后,整个程序却突然卡死了,哪里都不能点,在任务管理器查看,也不是没有响应,还是正常运行。等我重启调试时,依然是这样,并且旁边显示帧画面回调信息的两个按钮却能正常的开始和停止。

感觉自己解决不了这个问题,拉来leader帮我解决。一开始leader也纳闷,后来思考了一番,想到了问题所在——MFC的控件焦点机制导致的。

大前提:当前对话框是单线程的。 MFC程序在刷新某个控件时,是需要把焦点对准那个控件的。当我点击开始按钮时,焦点对准开始按钮,然后在回调函数里控件要执行刷新的动作,那么焦点必须对准编辑框。在点击关闭按钮时,焦点则要对准关闭按钮。本来一步一步来,大家相安无事,就像隔壁的帧画面回调信息。

问题的主要原因在于,码流回调信息实在是太多了,多到1毫秒一个数据的程度。导致的结果便是控件焦点几乎一直被编辑框所占据。当我点击关闭按钮前一瞬间,程序有特别大的概率执行到str,format("--- the buffer length is [%l] ---", param1); 这一行;而点击关闭按钮的那一瞬间,控件焦点对准关闭按钮,此时编辑框失去了焦点,不能执行(也就没有返回),必须等待获得焦点才能继续执行。点击关闭按钮后本应该执行setCallBackFunction(…)来关闭回调,却因为此时回调函数没有返回退出而必须等待回调函数返回退出。综合来说,就是点击关闭按钮后的一瞬间,程序陷入了编辑框等待焦点来刷新数据并返回退出,而关闭回调函数又在等待前一个码流回调函数返回退出而不能执行完毕释放焦点,双方你等我我等你,互相僵持的状态。结果便是程序卡死了。

由于帧画面回调信息是几十毫秒或者几百毫秒接收一次,间隔时间长,因此碰到上述的情况概率比较小,但也不是没有(多次实验帧画面回调同样会出现卡死的情况)。

  1. 解决方法

另开一个线程专门只进行编辑框的数据刷新,回调函数在获取完数据后立刻退出