将消息发送到属于另一个线程的窗口。可能死锁?

时间:2013-06-13 11:32:28

标签: c++ windows deadlock

我认为我正在陷入僵局,一直在寻找解决方案。有什么建议? 我想要做的是:ater startGame按钮单击,创建线程,发送请求到服务器,然后得到答案,在答案后线程必须发送消息初始化游戏窗口到主过程...

属于WinMain的Message Proc:

LRESULT CALLBACK WndProc(HWND myWindow, UINT messg, WPARAM wParam, LPARAM lParam)
   {
        switch (messg) {
               case WM_STARTGAME:
            DestroyWindow(hStartGameButton);
            DestroyWindow(hHistoryButton);

            InitGameWindow(myWindow);
        break;


                case WM_COMMAND:

                  switch(LOWORD(wParam))
                  {
                        case IDC_STARTGAME_BUTTON:
                        {
                          parametros param;
                          param.myWindow = myWindow;

                          start_game = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadStartGame, (LPVOID)&myWindow, 0, NULL);
                        }

                  }
   }

这就是主题:

DWORD WINAPI ThreadStartGame(LPVOID param){
HWND w = (HWND)param;
DWORD n;
BOOL ret;
mensagem resposta;


mensagem msg;
msg.tipo = COMECAR_JOGO;
msg.verifica = true;

if (!WriteFile(hPipe, &msg, (DWORD)sizeof(mensagem), &n, NULL)) {return 0;}

    ret = ReadFile(hPipeN, &resposta, (DWORD)sizeof(mensagem), &n, NULL);
    if (!ret || !n) {
        return false;
     }

PostMessage(w, WM_STARTGAME, NULL, NULL); // <- THIS GETS EXECUTED BUT NOTHINK HAPPENS AFTER

return 0;
}

3 个答案:

答案 0 :(得分:3)

我认为这里没有任何僵局。

start_game = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadStartGame, (LPVOID)&myWindow, 0, NULL);

该行将HWND的地址传递给线程(&amp; myWindow)

HWND w = (HWND)param;

此行使用地址本身作为HWND,SendMessage将消息发送到此地址,而不是HWND。

尝试修改为

start_game = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadStartGame, (LPVOID)myWindow, 0, NULL);

答案 1 :(得分:2)

即使查看代码,我也可以马上告诉你:不要在线程之间使用SendMessage。我建议您阅读Psychic debugging: The first step in diagnosing a deadlock is a simple matter of following the moneyPreventing Hangs in Windows Applications

  

在您的UI线程中使用异步窗口消息API ,尤其是将SendMessage替换为其中一个非阻塞对等PostMessageSendNotifyMessageSendMessageCallback

     

...   跨越线程边界的任何阻塞调用都具有可导致死锁的同步属性。调用线程使用'acquire'语义执行操作,并且在目标线程'释放'该调用之前不能解除阻塞。相当多的User32函数(,例如SendMessage ),以及许多阻止COM调用属于此类。

答案 2 :(得分:1)

对于初学者来说,你不可能首先做到这一点。引用MSDN

  

调用C运行时库(CRT)的可执行文件中的线程应使用_beginthreadex和_endthreadex函数进行线程管理,而不是CreateThread和ExitThread;这需要使用CRT的多线程版本。如果使用CreateThread创建的线程调用CRT,CRT可能会在内存不足的情况下终止进程。

其次,你的线程可以是worker或UI线程,从第一种类型你不能调用大多数与窗口相关的函数,因为它没有消息泵。 DestroyWindow就是这样。 (很多次我尝试使用MessageBox,尽管我自己的评论中有几行表示它在该函数中被禁止;)。

从工作线程,通常的方法是使用PostThreadMessage并对UI线程做出反应。 (如果你有多个UI线程,我不知道规则,对此没有足够的勇气。)