在非主執行緒中建立視窗
很多朋友都會有過這樣的經歷,為什麼在主執行緒中建立視窗且視窗工作很正常,但一移到非主執行緒(有的朋友喜歡叫它為工作執行緒),卻無法正常工作.本文就這個問題和各位探討,可能無法做到盡善盡美,但能拋磚引玉也算是欣慰了.
在主執行緒中建立一個能夠正常工作的視窗,估計地球人都知道.
這是一段工作正常的程式碼:
#include "windows.h" HWND g_hWnd = NULL; HINSTANCE g_hInst; LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam) { return DefWindowProc(hWnd,wMsg,wParam,lParam); } void CreateWnd(void) { WNDCLASS wc = {0}; wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_hInst; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW); wc.lpszMenuName = NULL; wc.lpszClassName = TEXT("SimpleWindow"); RegisterClass(&wc); g_hWnd = CreateWindowEx(0, TEXT("SimpleWindow"), TEXT("SimpleWindow"), WS_VISIBLE, 0, 0, 200, 200, NULL, NULL, g_hInst, 0); } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. g_hInst = hInstance; CreateWnd(); //The message loop MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
如果我們建立一個執行緒,然後在這個執行緒中建立視窗,看看帶給我們的是什麼:
#include "windows.h" HWND g_hWnd = NULL; HINSTANCE g_hInst; LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam) { return DefWindowProc(hWnd,wMsg,wParam,lParam); } void CreateWnd(void) { WNDCLASS wc = {0}; wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_hInst; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW); wc.lpszMenuName = NULL; wc.lpszClassName = TEXT("SimpleWindow"); RegisterClass(&wc); g_hWnd = CreateWindowEx(0, TEXT("SimpleWindow"), TEXT("SimpleWindow"), WS_VISIBLE, 0, 0, 200, 200, NULL, NULL, g_hInst, 0); } DWORD CreateThread(PVOID pArg) { CreateWnd(); return 0; } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. g_hInst = hInstance; HANDLE hThrd = CreateThread(NULL,0,CreateThread,NULL,0,NULL); CloseHandle(hThrd); //The message loop MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
我們似乎什麼都沒見到,只是視窗一閃,啥都沒了.因為g_hWnd為全域性變數,我們的理智告訴我們,在主執行緒沒有退出之前,g_hWnd是不會銷燬的.而用斷點除錯,將會發現在WndProc函式中只能接收WM_CREATE及以後一些訊息,之後的再也收不到了,特別是WM_PAINT似乎就憑空消失了!那麼,程式碼什麼都沒變更,只是移動到了分執行緒中,為何會出現這個問題呢?
一切似乎很簡單,在MSDN中我們找到了答案(原文見:http://support.microsoft.com/kb/90975/en-us):
In a multithreaded application, any thread can call the CreateWindow() API to create a window. There are no restrictions on which thread(s) can create windows.
It is important to note that the message loop and window procedure for the window must be in the thread that created the window. If a different thread creates the window, the window won't get messages from DispatchMessage(), but will get messages from other sources. Therefore, the window will appear but won't show activation or repaint, cannot be moved, won't receive mouse messages, and so on.
該段話大意是:視窗在任何執行緒中都可以建立,但訊息迴圈必須要和建立視窗在同一執行緒,否則視窗將無法從DispatchMessage()獲取任何訊息!
原來如此,最重要是這麼一句:It is important to note that the message loop and window procedure for the window must be in the thread that created the window.
好吧,那麼我們在支執行緒中放置訊息迴圈程式碼,看看是什麼結果吧:
#include "windows.h" HWND g_hWnd = NULL; HINSTANCE g_hInst; LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam) { return DefWindowProc(hWnd,wMsg,wParam,lParam); } void CreateWnd(void) { WNDCLASS wc = {0}; wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_hInst; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW); wc.lpszMenuName = NULL; wc.lpszClassName = TEXT("SimpleWindow"); RegisterClass(&wc); g_hWnd = CreateWindowEx(0, TEXT("SimpleWindow"), TEXT("SimpleWindow"), WS_VISIBLE, 0, 0, 200, 200, NULL, NULL, g_hInst, 0); } DWORD CreateThread(PVOID pArg) { CreateWnd(); //The message loop MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. g_hInst = hInstance; HANDLE hThrd = CreateThread(NULL,0,CreateThread,NULL,0,NULL); CloseHandle(hThrd); MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
一切正常,如同在主執行緒建立一樣!
當然了,還有點需要注意的,在這個例子中,由於訊息迴圈在主執行緒和分執行緒都分別存在,如果在WndProc()呼叫PostQuitMessage(),那麼退出的也僅僅是分執行緒,而主執行緒還是會不停地在等待訊息,從而導致程式無法正常退出.不過倒不用過分擔心,和這個示例程式碼不同,在實際程式碼編寫中,在主執行緒往往都會建立主視窗,而在這個主視窗訊息處理函式呼叫PostQuitMessage()則完全可以讓主執行緒正常退出.
事實告訴我們,非主執行緒建立視窗也能工作正常,只要我們注意一點:訊息迴圈必須要和建立視窗在同一執行緒!
相關文章
- 保證執行緒在主執行緒執行執行緒
- JAVA中執行緒的建立Java執行緒
- 怎樣在JavaScript中建立一個worker執行緒?JavaScript執行緒
- 執行緒的【生命週期】和【執行緒的同步】(多視窗售票例子)執行緒
- JAVA 程式 在 cmd 視窗的執行Java
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- pyqt5建立主視窗(介紹視窗型別)QT型別
- 在Mac中如何用⌘鍵拖拽非使用中的視窗?Mac
- 使用 Terminator 在一個視窗中執行多個終端
- 認識執行緒、建立執行緒寫法執行緒
- 執行緒池建立執行緒的過程執行緒
- 伺服器模型——從單執行緒阻塞到多執行緒非阻塞(中)伺服器模型執行緒
- 主佇列&主執行緒佇列執行緒
- Java併發(三)----建立執行緒的三種方式及檢視程式執行緒Java執行緒
- (MFC)子執行緒的資料如何傳遞給主執行緒中?執行緒
- QT 主執行緒子執行緒互相傳值QT執行緒
- 在netty3.x中存在兩種執行緒:boss執行緒和worker執行緒。Netty執行緒
- 子執行緒中建立Handler可以嗎?(上)執行緒
- 什麼是Python執行緒?Python執行緒如何建立?Python執行緒
- 漫畫:多執行緒經典例子之一視窗售票執行緒
- iOS 在主執行緒操作UI不能保證安全iOS執行緒UI
- Java多執行緒學習(1)建立執行緒與執行緒的生命週期Java執行緒
- performSelector:withObject:afterDelay: 在子執行緒中呼叫不執行performSelectorObject執行緒
- 非同步/同步,阻塞/非阻塞,單執行緒/多執行緒概念梳理非同步執行緒
- 模擬主執行緒等待子執行緒的過程執行緒
- 執行緒池建立方式執行緒
- jdk建立執行緒的方式JDK執行緒
- 使用CreateThreadPool建立執行緒池thread執行緒
- new Thread與執行緒建立thread執行緒
- dotnet 後臺執行緒傳送 X11 視窗訊息執行緒
- 在Python中用concurrent.futures建立執行緒池程序池Python執行緒
- 單執行緒-非阻塞-長連結執行緒
- 建立執行緒的4種方法 and 執行緒的生命週期執行緒
- 【java】【多執行緒】建立執行緒的兩種常用方式(2)Java執行緒
- Rust 程式設計視訊教程(進階)——016_2 建立執行緒與等待執行緒結束Rust程式設計執行緒
- 【併發技術01】傳統執行緒技術中建立執行緒的兩種方式執行緒
- Java 中執行緒池的7種建立方式!Java執行緒
- 主執行緒等待所有其他執行緒執行完畢,然後再繼續執行主執行緒的邏輯,有以下幾種方法可以實現:執行緒