rt,问题描述:
程序分为左边的Treectrl和右边的ListCtrl两部分,现在要求点击treectrl的某个节点后,通过查询数据库,将其对应的内容显示在右边的ListCtrl中,由于包含的内容比较多,所以为了让程序有较好的响应效果,我开了一个线程专门用来查询数据库,并且往listctrl中插入内容。当用户点击treectrl某个节点,线程开始查询数据库,并向listctrl中插入,如果此时用户又点击一个新节点,则正在运行的进程退出,然后新的进程开启。于是这就涉及到一个线程同步的问题,我用以下的方法://全局变量
BOOL g_end=FALSE;
HANDLE g_hThread=NULL;
//查询数据库并向listctrl中插入内容的线程
DWORD WINAPI MyThread(LPVOID lParam)
{
  //查询数据库,得到数据库中记录条数count
  //循环插入数据库
  for (int i=0;i<count;i++)
   {
      if (g_end)//如果此时有新的线程要创建,则不继续往listctrl中插入
              //记录,程序提前退出
       {
         //释放资源并退出
       }
     //否则向Listctrl插入下一条记录
    plistctrl->InsertItem()
    plistctrl->SetItem();
    ....    }}
//响应树视的Click事件的函数
void ***::OnClickTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
    if (g_hThread!=NULL) //如果有线程存在,先等线程结束
   {
       g_end=TRUE;
       WaitForSingleObject(g_hThread,INFINITEW);
       CloseHandle(g_hThread);
       g_hThread=NULL;
   
    }
    //然后再开始新线程
    g_end=FALSE;
    g_hThread=CreateThread(NULL,0,myworkthread,...,....);//创建线程}以前也类似做过,效果很好(不过以前线程是纯粹处理数据的,完全不与界面打交道),按道理来说,应该没有什么问题,但是此程序中每次到WaitForSingleObject(g_hThread,INFINITEW);都会死锁。后来考虑到是否thread里面涉及到窗口了(listctrl),而不是单纯的工作线程,而且看MSDN中有如下描述:Use caution when calling the wait functions and code that directly or indirectly creates windows. If a thread creates any windows, it must process messages. Message broadcasts are sent to all windows in the system. A thread that uses a wait function with no time-out interval may cause the system to become deadlocked。不知道是否与这个原因有关,还望各位指点,谢谢了,呵呵

解决方案 »

  1.   

    我想这样是否可以:
    要查询的内容很多:你可以获得前100条或者200条记录然后显示.处理CListCtrl滚动条事件他超过你已经查出的结果然后继续查。
      

  2.   

    我建议:不要每次关闭,创建线程。这其实是无用功。他们不都是一样那个的。你的WaitForSingleObject在线程函数中写。例如:
    DWORD WINAPI MyThread(LPVOID lParam)
    {
      //查询数据库,得到数据库中记录条数count
      //循环插入数据库
      while(true)
       {
        dwRst = WaitForMultipleObjects();//他的事件句柄至少有两个,1:tree点击事件,程序退出事件。
        switch(dwRst)//分别处理响应的事件。
        {
         }   // plistctrl->InsertItem() //处理点击事件的时候添加item。
       //  plistctrl->SetItem();
        ....    }}
      

  3.   

    to  Pipi0714(小兔子),谢谢,我考虑过你说的这样。
    但是还是和我说的目的有所不同啊
      

  4.   

    to  Pipi0714(小兔子),你的方法我有点不大明白啊:
    DWORD WINAPI MyThread(LPVOID lParam)
    {
      //查询数据库,得到数据库中记录条数count
      //循环插入数据库
      while(true)
       {
        dwRst = WaitForMultipleObjects();//他的事件句柄至少有两个,1:tree点击事件,程序退出事件。
        switch(dwRst)//分别处理响应的事件。
        {
         }   // plistctrl->InsertItem() //处理点击事件的时候添加item。
       //  plistctrl->SetItem();
        ....    }}是每插入一条记录就WaitForMultipleObjects();还是一次点击才WaitForMultipleObjects();啊,而且我感觉这两种情况都达不到目的啊,还望指教
      

  5.   

    当有需要与GUI交互的线程互锁需要特别小心个人不太同意楼主每次点击创建线程的做法,这样线程的频繁切换很容易出问题,应该统一创建一个全局点击处理线程
    当客户点击另一ITEM时,通知线程处理,在该线程中自当知道如果上次连接数据库未完成需要先取消,然后再连接新的ITEM对应的数据库,甚至如果是同一数据库,可以维持SESSION的不关闭。
      

  6.   

    to modena(非云),谢谢你的思路,如果只创建一个线程,那么线程应该是个死循环,并且在大多数时间是sleep()吧,而且我觉得如果只创建一个线程,一直运行的话,很可能需要goto语句来进行跳转,所以我感觉比每次点击创建线程简单不到哪里去啊
    还有一点,如果抛开其他思路不说,仅对于我的思路,请问应该如何做,才能解决我在上面所描述的问题呢,谢谢了
      

  7.   

    你在主线程中WaitForSingleObject等待,但你的MyThread线程用了plistctrl->InsertItem(),InsertItem()内部实际是调用API发送消息,而这些消息需要主线程来处理。InsertItem等待主线程处理消息,而主线程等待MyThread退出,所以程序就死掉了。M$为了解决这个问题,提供了MsgWaitForMultipleObjects,你可以试试。
      

  8.   

    to Cowboy22(西部牛仔) ,这位大侠,我也是这么分析的,但是我看了MsgWaitForMultipleObjects以后,觉得它不怎么好使啊,能指点一二么
      

  9.   

    对于你的代码,你的MyThread内是需要互锁的。
    我们这样说,假设你的代码执行到WaitFor...时,你创建的线程正好执行到plistctrl->InsertItem()之前一句汇编,此时WaitFor...将阻塞GUI线程,也就意味着InsertItem这一GUI操作无法继续进行,因而线程无法退出,那么WaitFor...将永远等下去了
      

  10.   

    另外更正一点,如果一个线程是以死循环的方式出现,并且大部分时间Sleep,那么绝对不是一个正确的线程。而  Pipi0714(小兔子)   提供的线程模板完全可以解决这个问题
      

  11.   

    to modena(非云),谢谢你的解答,我想可能程序就是你所描述的那样产生死锁的但是,另外,我觉得“如果一个线程是以死循环的方式出现,并且大部分时间Sleep”,它应该还是一个正确的线程,因为虽然是死循环,但是它大部分时间都sleep,所以不会占用cpu时间,而且Pipi0714(小兔子)实际也有一个死循环在。另外,我不明白,Pipi0714(小兔子)的线程模板的意思,首先,他是每插入一条记录,就执行WaitForMultipleObjects()呢,如果是这样的话,那么必须用户每次都点击treectrl,否则线程是不会继续下去的;如果是每次插入所有记录之前执行WaitForMultipleObjects(),那么当线程正在插入记录的时候,此时用户如果点击另外一个节点,线程根本不能及时响应啊。所以我不大明白,还望指教,谢谢
      

  12.   

    呵呵,我觉得Sleep分两种,一种占用CPU时间,一种不占用,但说实在的我也不知道Sleep(1000)会不会占用CPU时间个人觉得 Pipi0714(小兔子) 的线程写法是正确的,如果没有WaitForMultipleObjects,那么该线程无法被外部控制,至于你外部要求该线程所做的事情是在该句之后执行的另外对于你目前程序的解决方法同意  Cowboy22(西部牛仔) 的方法。
    我从MSDN中获取这段关于MsgWaitForMultipleObjects的使用示例,希望能对你有用:
      

  13.   

    int MessageLoop ( 
        HANDLE* lphObjects,  // handles that need to be waited on 
        int     cObjects     // number of handles to wait on 
      )

        // The message loop lasts until we get a WM_QUIT message,
        // upon which we shall return from the function.
        while (TRUE)
        {
            // block-local variable 
            DWORD result ; 
            MSG msg ;         // Read all of the messages in this next loop, 
            // removing each message as we read it.
            while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
            { 
                // If it is a quit message, exit.
                if (msg.message == WM_QUIT)  
                    return 1; 
                // Otherwise, dispatch the message.
                DispatchMessage(&msg); 
            } // End of PeekMessage while loop.        // Wait for any message sent or posted to this queue 
            // or for one of the passed handles be set to signaled.
            result = MsgWaitForMultipleObjects(cObjects, lphObjects, 
                     FALSE, INFINITE, QS_ALLINPUT);         // The result tells us the type of event we have.
            if (result == (WAIT_OBJECT_0 + cObjects))
            {
                // New messages have arrived. 
                // Continue to the top of the always while loop to 
                // dispatch them and resume waiting.
                continue;
            } 
            else 
            { 
                // One of the handles became signaled. 
                DoStuff (result - WAIT_OBJECT_0) ; 
            } // End of else clause.
        } // End of the always while loop. 
    } // End of function.
      

  14.   

    to modena(非云),线程调用Sleep()后,就会立即退出cpu,放弃剩余的时间片,所以我觉得不存在占用cpu的sleep和不占用cpu的sleep之分。另外,Pipi0714(小兔子)的写法如何解决我的问题啊,能否说得清楚一点,我觉得如果是插入记录之前才调用WaitForMultipleObjects()的话,那么此时线程正在插入记录,然后用户点击另外一个节点,那么只有等待记录插完以后,才可能响应用户的点击事件了。如果是这样,就不用开线程了,直接在treectrl的点击响应事件中做就可以了
      

  15.   

    to modena(非云) ,这个例子我也看过了啊,但是好像和我的程序要求有所不同啊,而且也不好借鉴,ft
      

  16.   

    Pipi0714(小兔子)的方法是针对“不要每次关闭,创建线程”而写的吧
      

  17.   

    把你的:
           WaitForSingleObject(g_hThread,INFINITEW);
           CloseHandle(g_hThread);
           g_hThread=NULL;
    改成:
           MessageLoop (&g_hThread, 1);
    把 modena(非云) 的MessageLoop ()中的DoStuff (result - WAIT_OBJECT_0) ;改成:
           CloseHandle(g_hThread);
           g_hThread=NULL;
           break;
    试试看。
      

  18.   

    to Cowboy22(西部牛仔),哈哈,^_^,谢了,^_^
    解决了,我怎么没有想到,呵呵,看了半天,我还以为它是个消息循环呢,呵呵,不好意思,呵呵,再次感谢你!哈哈,好高兴,这个问题一直折磨我,一直没有得到很好的解决,今天终于解决了,谢谢楼上的各位,散分了,呵呵!!