将 GetDC 代码移动到线程中时性能大幅下降

Posted

技术标签:

【中文标题】将 GetDC 代码移动到线程中时性能大幅下降【英文标题】:Great Performance Decreasement when Moving the GetDC code into a thread 【发布时间】:2021-07-12 15:55:41 【问题描述】:

对于Strange Efficiency Decrease when Localizing a Global Variable into a Subthread的下一个问题,最近我发现性能下降的问题实际上是由于将GetDC代码移动到一个线程中引起的,它最初是放在一个WindowProc/WndProc函数中的,但我不知道与细节。

谁能帮帮我?

毕竟,这是我的代码:

快速:

#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);

慢一:

#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);

【问题讨论】:

HWND 不是指针(8 字节?)?尝试以 64 字节对齐方式传递数据。 您正在将一个指向局部变量的指针传递给您的 threadProc。尝试按值传递。还要描述您如何衡量绩效。 为什么你在 CreatePen 而不是在 SelectObjectMoveToEx 中决定该任务?这2个api(和LineTo)与设备上下文一起工作,属于窗口,窗口属于线程。如果你从另一个线程调用这个 api - 需要与这里的所有者(主)线程同步 - 你不这么认为吗?真正将此代码移动到工作线程只会降低性能 【参考方案1】:

您正在将一个指向局部变量的指针传递给您的threadProc。从WindowProc 退出后,它的局部变量不再有效。 你可以通过值传递HWND

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

然后读取它的值:

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

【讨论】:

您的线程在一个以太循环中使用大量 CPU 时间绘制线条。最好将所有绘图代码移动到WM_PAINT 处理程序。仅在需要重新绘制时调用此处理程序。 我发现如果hwnd为NULL,效果也很慢。当 hwnd 为 NULL 时,windows.h 可能会创建另一个窗口? 得到它! The document 表示如果此值(hWnd)为 NULL,GetDC 将检索整个屏幕的 DC。

以上是关于将 GetDC 代码移动到线程中时性能大幅下降的主要内容,如果未能解决你的问题,请参考以下文章

左连接会导致性能大幅下降。如何修复它

Python实现简单多线程任务队列

QT 引用之前项目模板导致计算速度严重下降

[PHP]Yaf + composer 引起大幅性能下降

JavaScript 引擎「V8」发布 8.0 版本,内存占用量大幅下降

巨大的性能下降 - 向量问题?