Search code examples
cwindowsmultithreadinguser-interfacegdi

Great Performance Decreasement when Moving the GetDC code into a thread


For the next question of Strange Efficiency Decrease when Localizing a Global Variable into a Subthread, resently I found the question of the performance decreasement is actually caused by moving the GetDC code into a thread, where it was originally placed in a WindowProc/WndProc function, but I have no idea with the detail.

Could anyone help me?

After all, here is my code:

Quick one:

#include <stdio.h>
#include <math.h>
#include <windows.h>
#define num 4
#define width 1
double position[num][2] = {{733, 434}, {633, 384}, {733, 284}, {733, 684}};
double velocity[num][2] = {{-5, 2.5}, {5, -2.5}, {0, 2.5}, {10, -2.5}};
COLORREF color[num] = {0xffffff, 0x00ffff, 0x0000ff, 0xffff00};
COLORREF background_color = 0x000000;
double distance;
int count, i, j;
HDC hdc[num];
HPEN hpen[num];
inline double sqr(double x) {return x * x;}
DWORD WINAPI threadProc(LPVOID lpParamter) {
    Sleep(1000);
    while(1) {
        for(i=0; i<num; ++i) {
            LineTo(hdc[i], position[i][0], position[i][1]);
            position[i][0] += velocity[i][0];
            position[i][1] += velocity[i][1];
        }
    }
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR pCmdLine, int nCmdShow) {
    const char *CLASS_NAME = "SUGEWND";
    void *pHWND = &hInstance;
    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    RegisterClass(&wc);
    HWND hwnd = CreateWindowEx(0, CLASS_NAME, "SUGE", 
    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
    CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    if(hwnd == NULL) return 0;
    ShowWindow(hwnd, SW_SHOWMAXIMIZED);
    MSG msg={};
    while(GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch(uMsg) {
        case WM_DESTROY: {
            PostQuitMessage(0);
            return 0;
        }
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc2=BeginPaint(hwnd, &ps);
            FillRect(hdc2, &ps.rcPaint, CreateSolidBrush(background_color));
            EndPaint(hwnd, &ps);
            return 0;
        }
        case WM_CREATE: {
            for(i=0; i<num; ++i) {
                hdc[i] = GetDC(hwnd);
                hpen[i] = CreatePen(PS_SOLID, width, color[i]);
                SelectObject(hdc[i], hpen[i]);
                MoveToEx(hdc[i], position[i][0], position[i][1], 0);
            }
            HANDLE hThread = CreateThread(NULL, 0, threadProc, NULL, 0, NULL);
            CloseHandle(hThread);
            return 0;
        }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Slow one:

#include <stdio.h>
#include <math.h>
#include <windows.h>
#define num 4
#define width 1
double position[num][2] = {{733, 434}, {633, 384}, {733, 284}, {733, 684}};
double velocity[num][2] = {{-5, 2.5}, {5, -2.5}, {0, 2.5}, {10, -2.5}};
COLORREF color[num] = {0xffffff, 0x00ffff, 0x0000ff, 0xffff00};
COLORREF background_color = 0x000000;
double simulate_acc = 0.00001;
double distance;
int count, i, j;
HDC hdc[num];
HPEN hpen[num];
inline double sqr(double x) {return x * x;}
DWORD WINAPI threadProc(LPVOID lpParamter) {
    HWND hwnd = *(HWND*)lpParamter;
    for(i=0; i<num; ++i) {
        hdc[i] = GetDC(hwnd);
        hpen[i] = CreatePen(PS_SOLID, width, color[i]);
        SelectObject(hdc[i], hpen[i]);
        MoveToEx(hdc[i], position[i][0], position[i][1], 0);
    }
    Sleep(1000);
    while(1) {
        for(i=0; i<num; ++i) {
            LineTo(hdc[i], position[i][0], position[i][1]);
            position[i][0] += velocity[i][0];
            position[i][1] += velocity[i][1];
        }
    }
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR pCmdLine, int nCmdShow) {
    const char *CLASS_NAME = "SUGEWND";
    void *pHWND = &hInstance;
    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    RegisterClass(&wc);
    HWND hwnd = CreateWindowEx(0, CLASS_NAME, "SUGE", 
    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
    CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    if(hwnd == NULL) return 0;
    ShowWindow(hwnd, SW_SHOWMAXIMIZED);
    MSG msg={};
    while(GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch(uMsg) {
        case WM_DESTROY: {
            PostQuitMessage(0);
            return 0;
        }
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc2=BeginPaint(hwnd, &ps);
            FillRect(hdc2, &ps.rcPaint, CreateSolidBrush(background_color));
            EndPaint(hwnd, &ps);
            return 0;
        }
        case WM_CREATE: {
            HANDLE hThread = CreateThread(NULL, 0, threadProc, &hwnd, 0, NULL);
            CloseHandle(hThread);
            return 0;
        }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Solution

  • You are passing a pointer to a local variable to your threadProc. After exiting from WindowProc it's local variables are no more valid. You can pass HWND by value:

    case WM_CREATE: {
        HANDLE hThread = CreateThread(NULL, 0, threadProc, (LPVOID)hwnd, 0, NULL);
        CloseHandle(hThread);
        return 0;
      }
    

    and read it's value so:

    DWORD WINAPI threadProc(LPVOID lpParamter) {
      HWND hwnd = (HWND)lpParamter;