首先声明的是,现在程序已经能基本运行起来了,但偶尔会出现一些莫名其妙的错误,百思不得其解,麻烦各位了,
在我程序中,参考了很多网友的代码,在此谢过各位网友了,但是有很多地方不是很明白,烦请大家帮忙解解疑。
程序的工作原理和流程:
每建立一个CDlgInfo对话框,都启一个定时器(定时器11,每隔2s定时一次),然后这个定时器判断当前是否有扫描底层数据
的线程在运行,如果没有则开启一个线程,否则什么也不做,等待下一次定时消息,再判断,如果已经有线程运行过,并已经
结束了,则释放以前线程的资源,并且再启一个线程继续扫描。
当开启扫描底层数据的线程后,则给底层发送数据,如果底层设备是有电的,则它将返回相应的数据给上层,如此循环执行下去,
如果某个时刻底层设备没电了(通过底层是否返回数据了来判断),则退出循环,并结束当前线程,在此期间,当线程启动时,并
开启一个定时器,来不断地刷新当前对话框的内容,如果当前设备没电了,则不再刷新。此外,上层发送数据后,底层会返回相应
的数据,这时候程序会接收到(wMsg消息,这是自定义的),紧接着就会触发OnRecv函数来接收数据并将数据送到指定的位置。现在有几个问题想问问大家,请大家帮帮忙,在此谢过了.
1.整个程序的组织有没有什么不正确的地方,该怎么修改,特别是关于线程的地方。(这个问题是最关键的问题)2.下面就涉及到细节问题了,在线程函数ScanThread里的pDlg->SetTimer(13,100,NULL);这个地方我启动了一个定时器
这个定时器是否有主线程来执行,消息也是由主线程来接收,在这个线程函数里,是不是要做些额外的工作(比如转发消息)3.发送数据后,底层返回相应的数据,这时候触发的wMsg消息是不是也由主线程来接收还是ScanThread线程接收,我在等待
ScanThread线程接收数据时,转发了消息,是不是起了作用?最搞不懂的是,我现在的线程是个工作线程,这里面的消息究竟是怎么传递的,
怎么接收的?4.在ScanThread线程中,为了以后能够让主线程也能发送一些包到底层数据和防止ScanThread多次启动执行(同时只能由一个线程向底层发送
数据,并且发送数据后必须等待一段时间来接收数据,虽然这个时间很短,但不能再有数据发送下去),我这里用了一个信号量来做同步和互斥,
不知可否,是不是要采用一些其他的同步对象,比如临界区,互斥等。5.在OnDestroy()里while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 

DispatchMessage(&msg); 
}
为什么要加上这一句呢,MsgWaitForMultipleObjects不是自己可以接收转发消息吗?6.这个问题呆会儿加

程序清单:#define wMsg WM_USER+5 //用于接收底层数据
class CDlgInfo : public CDialog
{
// Construction
public:
     BOOL m_bJijiaFlag;
     CSemaphore *sem;//信号量
CMainFrame *m_pParent;//当前线程指针
static UINT ScanThread(LPVOID pParam);//自己的线程函数

volatile BOOL m_bPowerFlag;//当前设备是否有电标志位
void OnRecv(WPARAM wParam, LPARAM lParam);//接收底层数据的消息接收函数


//省略若干
} BOOL CDlgInfo::OnInitDialog() 
{
CDialog::OnInitDialog();

// TODO: Add extra initialization here
        m_bPowerFlag=FALSE;//刚开始,设备是没电的
        
        pScanThread=NULL;//线程指针置为空
        
        m_bReceived=FALSE;
        
        sem=new CSemaphore();//申请一个信号量资源
        
    //启动定时器,扫描设备是否存在,如果存在,即开启扫描线程
     SetTimer(11,2000,NULL);

return TRUE;  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}
UINT CDlgInfo::ScanThread( LPVOID pParam)
{
CDlgInfo *pDlg=(CDlgInfo *)pParam;
        //创建一个CSingleLock对象,初始化为信号量sem
CSingleLock singlock(pDlg->sem);

//当前还未刷新界面
pDlg->m_bRefreshFlag=FALSE;

do
{
        //锁信号量
        singlock.Lock();

//发送snmp包到底层
pDlg->SendSnmp(); //当前设备有电,并且当前还未刷新界面,则开启定时器13,没隔100ms刷新一次
if((pDlg->m_bPowerFlag==TRUE)&&(pDlg->m_bRefreshFlag==FALSE))
        {
                pDlg->m_bRefreshFlag=TRUE;//已经开启定时器刷新界面了,不需要再次开启了 
pDlg->SetTimer(13,100,NULL);

        } 
//开信号量
singlock.Unlock();
//休眠1000ms
Sleep(1000);
}
while(pDlg->m_bPowerFlag==TRUE);//当前设备还有电,则继续扫描        pDlg->KillTimer(13);//线程即将退出,将刷新界面定时器关掉,不再刷新界面了
    
return 0;
}

解决方案 »

  1.   

    void CDlgInfo::OnTimer(UINT nIDEvent) 
    {
    // TODO: Add your message handler code here and/or call default
    switch (nIDEvent)
    {
         case 11:
    if(pScanThread==NULL)//第一次启动
    {
      pScanThread=AfxBeginThread(ScanThread,this);//启动线程
    pScanThread->m_bAutoDelete=FALSE;//线程为手动删除


    }
    //当前线程没有启动或已结束,则启动当前线程
    else if(WaitForSingleObject(pScanThread->m_hThread,0)==WAIT_OBJECT_0)
             { 
    KillTimer(11);
    //删除先前线程的资源
         if(pScanThread!=NULL)
         {
    delete pScanThread;//删除线程
    pScanThread=NULL;
           
         }

    //启动线程
             pScanThread=AfxBeginThread(ScanThread,this);
    pScanThread->m_bAutoDelete=FALSE;//线程为手动删除


    SetTimer(11,2000,NULL);

            }
        case 12://一次查询结束
            //查询机架信息时超时,则表示设备没电了 
    if(m_bJijiaFlag==TRUE)
    m_bPowerFlag=FALSE;
    KillTimer(12);
         m_bReceived=TRUE;
    break;
        
    case 13:
            //不断地刷新界面
    //m_bFlashFlag=!m_bFlashFlag;
    InvalidateRect(CRect(0,50,ptRect[16].right,ptRect[16].bottom),FALSE);
      }
    CDialog::OnTimer(nIDEvent);
    } void CDlgInfo::OnPaint() 
    {
    CPaintDC dc(this); // device context for painting

    // TODO: Add your message handler code here

    //在这里画图(画在对话框上,根据从底层获取的数据的不同,画的内容也不同)
    }void CDlgInfo::SendSnmp()
    { int i;
    //查询底层信息

    m_bJijiaFlag=TRUE;//当前正在查询机架

    /**********这里是发送snmp包到底层,当底层收到数据之后,接着立刻返回相应的数据,
    在我程序里是通过 recv消息的接收函数OnRecv来实现的*************/
    if(pSnmp.sessionID==FALSE)
    {
    pSnmp.CreateSession(m_hWnd,wMsg);
    pSnmp.sessionID=TRUE;
    }
    pSnmp.CreateVbl("1.3.6.1.4.1.991.1.1.0",NULL);
    pSnmp.SetVbl("1.3.6.1.4.1.991.1.2.0",NULL);
    pSnmp.SetVbl("1.3.6.1.4.1.991.1.3.0",NULL);
    pSnmp.SetVbl("1.3.6.1.4.1.991.1.4.0",NULL);
    pSnmp.CreatePdu(SNMP_PDU_GET,NULL,NULL,NULL);
    pSnmp.Send(m_sIP,"public");
    /******snmp发送完毕*****/        //发送后等待一段时间,等待接收数据
    SendDelay(100);

      m_bJijiaFlag=FALSE;//机架信息查询完毕
        }
    void CDlgInfo::SendDelay(int delaytime)
    {
       //设置当前还未收到底层的数据
       m_bReceived=FALSE; 
       
       //设置12号定时器来使当时间过了200ms还未收到数据后,使m_bReceived变为真,以便下一轮的发送
       KillTimer(12);
       SetTimer(12,delaytime,NULL);
       
        MSG message; /*等待收取数据,如果数据还未收到,则等待,(过了200ms后还未收到数据后,
    12号定时器,会强制使数据接收标志置为真,从而退出循环,准备下一轮发送
    )在这里表现为转发消息*/
    while(m_bReceived==FALSE)
    {
    if(::PeekMessage(&message,NULL,0,0,PM_REMOVE))
    {
    ::TranslateMessage(&message);
    ::DispatchMessage(&message);
    }
    }
       
    }void CDlgInfo::OnRecv(WPARAM wParam, LPARAM lParam)
    { CString strIp;
    CString strTemp;
    int nIpin=0;//保存接收的整数值
    CString str="";//保存接收的字符串

    pSnmp.Receive(m_sOid,m_value); if(pSnmp.nCount>0)//收到了数据包
    {
    m_bPowerFlag=TRUE;
    Module[16].m_bPowerFlag=TRUE; for(int i=0;i<pSnmp.nCount;i++)
    {
    //接收数据
    switch(m_value[i]->syntax)
    {
    case SNMP_SYNTAX_INT: 
    //case SNMP_SYNTAX_INT32:
    smiINT sNumber;
    sNumber=m_value[i]->value.sNumber;
    nIpin=sNumber;
    str.Format("%d",sNumber); break;
    //省略若干

    }
         
            //分析数据,并将数据存放到相应的地方去(存放到相应的数组中)  }

    //接收数据标志位置为真
                    m_bReceived=TRUE;
      //关闭12号定时器,不需要判断数据包接收超时了
      KillTimer(12);


    }
    }   void CDlgInfo::OnDestory()
    {
    //关闭所有的定时器
    KillTimer(11);
    KillTimer(12);
    KillTimer(13); if(pScanThread!=NULL)
    while (TRUE)
    {
    DWORD result; 
    MSG msg ; 
    m_bPowerFlag=FALSE;//改变变量,等待线程结束 
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 

    DispatchMessage(&msg); 
    }

    //判断线程是否结束,如果还未结束,则等待并转发消息,否则就退出程序了
    result = MsgWaitForMultipleObjects(1, &pScanThread->m_hThread, 
                      FALSE, INFINITE, QS_ALLINPUT); 
    if (result == (WAIT_OBJECT_0 + 1))
    {
    ::MessageBox(NULL, "STOP","结束线程not!",MB_OK);
                 continue;

    else 

    ::MessageBox(NULL, "STOP","成功结束线程!",MB_OK);
                 break;
    }
    }

        //删除线程资源
        if(pScanThread!=NULL)
        {
    delete pScanThread;//删除线程
    pScanThread=NULL;
           
        }

      

  2.   

    so 长的代码,不用调试的话是很痛苦的
    如果可以的话 [email protected]
      

  3.   

    感觉好乱,其实完全可以在你的ScanThread中都搞定的,为什么要弄这么多定时器呢
    没事关掉ScanThread干什么,一直开着好了,用WaitForSingleObject或Sleep来代替你的定时器吧,工作线程里Sleep不会影响你的主线程的
    在你的ScanThread里
    while(true){
      breceived = false;
      powerflag = false;
      while(!breceived){
      SendSnmp();
      Sleep(100);
      RecvSnmp();//收到的话breceived=true;powerflag=true;而且发送后就可以刷新了啊,为什么要用定//时器呢,不过用了也没关系!
      }
      if(!powerflag)Sleep(2000);
      else{
       //设置刷新定时器,如果需要的话
      }
    }
      

  4.   

    首先,我收消息的函数是不能自己调用的,有数据来的时候是发消息给OnRecv函数的
    其次,我刷新界面的屏幕要比你取完数据后再刷新的频率快,这主要是为了能让屏幕上某些灯能够一直在闪烁,如果采用在取完数据在刷新界面的画,就没有那种效果了,因为速度太满了
      

  5.   

    但是,为什么要发消息,再由消息处理OnRecv处理呢,你直接pDlg->Recv不就可以了,更何况,谁收消息的函数不能自己直接调用的
    刷新界面比取数据快,那不是说,你刷新所用的数据有可能是相同的,就我所知OnTimer本身精度才55ms,也快不了多少的
      

  6.   

    我用的这个snmp类,它接收数据是消息驱动的,下面有数据了,自然而然地,这个函数就被调用了虽然可能连续数据都是相同地,但在界面上我必须让某些灯一闪一灭地,这样才符合要求,但如果接收数据后在刷新,就必须要求发送接收数据的时间很快才行,实际上这个很难达到,而且如果这样的画,这个线程占用的cpu时间会很多的。
      

  7.   

    有点明白了,很显然
    那么你的2s的定时器,是不是有点多余了,你的ScanThread为什么要退出呢,每过1000ms判断是否有电,有电就发送数据,没电就不发好了,我甚至看不出你将ScanThread做成一个线程的必要性,弄个1000ms的定时器不就好了
      

  8.   

    谢谢 xiaoqiqixiao(七七)
    原来就是这样做的,但那样更新的速度太慢了,耗用cpu资源太多了,才把它改成多线程的,还请各位多帮帮忙,看看,改改