前两天曾经发过贴,请教如何解决界面切换困难的问题,听了一些朋友的建议,决定采用多线程解决,可是发现还是没效果,我把问题简单描述一下,请各位看客帮忙参谋参谋,多提宝贵意见。
  在一个基于CFormView的多文档程序中,当切换界面时有个界面反应很迟钝,后来发现是一个数据采集函数太耗时了,该函数执行完大约需要2~3秒。而且该函数还要每隔大约3秒钟就要被调用一次。后来我决定将该函数放到一个线程函数中执行,该函数执行完再将结果赋给一个全局的结构体数组。然后在主线程中用虚拟列表将保存在结构体数组中的数据显示到列表中。思路大致就是这样的,代码大致如下://原来的方案:void CPidHistgramView::ProcessData()  //约每3秒被调用一次
{
   ReceiveData();  //从动态库中读数据,耗时大约2~3秒
   
   m_virtualList.SetItemCount(m_listCount);  //设置列表中总的数据条目数  
}void CPidHistgramView::ReceiveData()
{
   for(int i=0;i<m_listCount;i++)
   {
     //调用库中函数获取数据,耗时约3秒
      .....
     .....
     ////
     //将得到的数据赋给全局结构体数组
       strcpy( g_data[i].str1,sValue1);
      strcpy( g_data[i].str2,sValue2);
      ...
      strcpy( g_data[i].str8,sValue8);
   }}  因为数据采集函数太耗时,所以将其放入到工作线程中,后来的采用方案在工作线程中建立一个消息队列,获取工作线程的WM_TIMER消息,每3秒发送一次。在工作线程的WM_TIMER响应函数里再调用全局函数ReceiveData()采集数据,代码大致如下:// 线程函数,在OnInitialUpdate()函数中调用 AfxBeginThread(ThreadFunc,(LPVOID)NULL)启动UINT ThreadFunc(LPVOID lParam)  
{
   int nTimerID=SetTimer(NULL,0,3000,NULL);  // 设定一个3秒间隔的定时器   MSG msg;
   PeekMessage(&msg,NULL,WM_USER,WM_USER,PM_NOREMOVE);
   
   DWORD dEndWord;
   BOOL bRun=TRUE;
   while (bRun)
   {
dEndWord=WaitForSingleObject(eventEnd,0);  //查询"结束事件"状态,立即返回
if(dEndWord==WAIT_OBJECT_0)  //可以正常退出
{

KillTimer(NULL,nTimerID);
return 0;
}

if (GetMessage(&msg,NULL,NULL,NULL))
{
switch(msg.message)
{

 case WM_TIMER:
 {
   ReceiveData();  //从动态库中读数据,耗时大约2~3秒  }
 break;

default:
break;

}
}
    }
    return 0;
}void CPidHistgramView::ProcessData()  //约每3秒被调用一次
{     
   m_virtualList.SetItemCount(m_listCount);  //设置列表中总的数据条目数  
}  主要代码如上所示,主要思路是程序运行时就启动工作线程,工作线程每3秒发送一个定时器消息,ReceiveData()采集函数大约要费时2秒多。ProcessData()函数每3秒被调用一次,用来刷新界面上的列表控件,显示时用到了CVirtualList Control 技术。  按照原来的设想,将采集函数放到工作线程后CPU应该会降下来,不会再阻塞主线程,可是运行发现,CPU的占有率还是很高,达到90%以上,调试发现主要问题出在ReceiveData()这个采集函数上,当把这个函数注销后,CPU就很低了。而且用虚拟列表显示时按说应该每调用一次,界面就一次完整的把数据显示出来,可现在发现,当每3秒调用ProcessData()函数刷新界面时,列表有时是由上到下一行一行显示数据,效果非常差,搞不清楚用了虚表怎么还会这样,难道是因为CPU太忙碌了的缘故?
  像我这种情况该怎么解决?

解决方案 »

  1.   

    在ReceiveData里面的循环体里面简单加一句Sleep(10);吧
      

  2.   

    CPU使用率高,说明你的程序执行的操作需要占用大量的CPU时间,利用多线程和设置线程优先级或者适当Sleep可以改善界面响应,但无法降低CPU使用率。这种情况只能从优化原理和优化代码方面来设法缓解了,如果无法缓解,并且CPU的空闲率为0%,就需要提高软件运行环境的要求了。
      

  3.   

    这种问题我也遇到过,Sleep只能稍微缓解CPU的高占用,而且会导致处理效率的降低。
    这种情况,必须优化你的接收处理过程ReceiveData,处理数据要比接收数据耗时的多,一定要优化数据处理的算法。
      

  4.   

    调试发现主要问题出在ReceiveData()这个采集函数上。
      

  5.   

    在ReceiveData里面的循环体里面简单加一句Sleep(10)吧
    ///这是一个好主意。我试过了,设置Sleep(30),界面效果稍有改善,但还是不能令人满意,设置Sleep(100),效果不错,切换也不太迟钝,但带来的问题是数据更新周期变长了,原来3秒多更新一次数据,现在有时要7秒多。真的很难两全啊。
      

  6.   

    其实最直接的办法应该是不使用定时器,不停循环扫描,每个循环体内只处理1条数据,然后用WaitForSingleObject,但是不要用0作为超时参数。如果当前循环中没有数据需要处理,则直接WaitForSingleObject,超时时间可以设置更长些
      

  7.   

    刚才又跟踪了一下程序,现在可以肯定耗时出在数据采集函数ReceiveData内的循环体上。在循环体内主要就是调用了一个
    switch case 多分枝语句,共有多条分枝。
    void CPidHistgramView::ReceiveData() 

              //循环体执行完大约3秒钟,该怎么改进结构?
      for(int i=0;i <m_listCount;i++) 
      { 
         
      switch( nPriority )  // 级别
      {
      case 1:
      ...
            break;
      case 2:
      ...
    break;
      case 3:
      ...
    break;
      case 4:
      ...
    break;
      case 5:
      ...
     break;
      case 6:
      ...
    break;
      case 7:
      ...
    break;
      case 8:
      ...
    break;
      default:
      break;
      }    
      //// 
      
      } 
      
      } 想请教各位大侠,像这种情况下,怎么改进结构才能提高运行效率?
      

  8.   

    switch结构中连续的分支在Release编译的时候会优化成跳转到指针变量的形式,不会因分支多而浪费时间。
      

  9.   

    试试这个
    SwitchToThread
      

  10.   

    数据采集放到单独线程中,可以加上延时函数,我一般是插入以下函数,会明显改善显示停顿问题:void _Sleep( INT iMS ) 
    {
    DWORD dwStart = GetTickCount() ;
        while( TRUE ) 
    {
    MSG msg;
    while( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
    {
    ::TranslateMessage( &msg );
    ::DispatchMessage( &msg );
    }

    if ( GetTickCount() - dwStart > (DWORD)iMS ) 
    {
    break ; 
    }
    }
    }优化采集算法,将采集分散在线程多次执行中,如果涉及到多点采集,可以使用多个线程进行
    界面刷新频率可以降低,一般2~3Hz就可以了