Windows XP 和 Windows 7 中的不同程序行为

Posted

技术标签:

【中文标题】Windows XP 和 Windows 7 中的不同程序行为【英文标题】:Different program behaviour in Windows XP and Windows 7 【发布时间】:2017-02-15 11:01:33 【问题描述】:

这段代码在两个线程中从窗口的顶部到底部绘制了两条垂直条纹(每个线程对应的函数是thread_func)。第一个线程绘制左侧条纹的一部分,然后第二个绘制右侧条纹的一部分,然后再次绘制第一个,依此类推。信号量和临界区用于确保此顺序。

#include <windows.h>
#include <cstdint>

HDC hDC; 
HDC  hDCMem;
HBITMAP hbitmap;
HWND  hwnd; 
int ScreenMaxX;
int ScreenMaxY;

short pattern[8]=~0xFF, ~0xFF, ~0xFF, ~0xFF, ~0xFF, ~0xFF, ~0xFF, ~0xFF;
HBRUSH brush=::CreatePatternBrush(::CreateBitmap(8, 8, 1, 1, pattern));

void bar(int nLeft, int nTop, int nRight, int nBottom)

    RECT rect;
    rect.left   = nLeft;
    rect.right  = nRight;
    rect.top    = nTop;
    rect.bottom = nBottom;

    ::SetTextColor(hDCMem, 0xFF00FF);
    ::SetBkColor(hDCMem, 0xFF00FF);
    //brush=::CreatePatternBrush(::CreateBitmap(8, 8, 1, 1, pattern));
    ::FillRect(hDCMem, &rect, brush);


void flush()
    ::BitBlt(hDC, 0, 0, ScreenMaxX, ScreenMaxY, hDCMem, 0, 0, SRCCOPY);


CRITICAL_SECTION graphics_cs;

uint8_t thread_cnt=0;
uint8_t total_threads=2;
HANDLE turnstile1=CreateSemaphoreW(nullptr, 0, 2, nullptr);

void thread_func(int num)
    int x,y;
    if(num==0)
        x=20; y=0;
     else 
        x=110; y=0;
    

    while(true) 
        while(true) 
            EnterCriticalSection(&graphics_cs);
            if (thread_cnt == num) 
                thread_cnt++;
                bar(x, y, x+40, y+40);
                y+=1;
                //flush();
                if(thread_cnt==total_threads)
                    thread_cnt = 0;
                    flush();
                    ReleaseSemaphore(turnstile1, total_threads, nullptr);
                
                LeaveCriticalSection(&graphics_cs);
                break;
             else 
                LeaveCriticalSection(&graphics_cs);
            
        

        WaitForSingleObject(turnstile1, INFINITE);

        Sleep(100);
    


void mainx ()

    InitializeCriticalSection(&graphics_cs);
    for(int i=0; i<total_threads; i++)
        CreateThread (nullptr, 0, (LPTHREAD_START_ROUTINE)thread_func, (LPVOID)i, 0, nullptr);
    


DWORD Th(LPVOID param)

    (void)param;
    ::SetWindowPos(hwnd, HWND_TOP,
                   10,
                   10,
                   400,
                   500,
                   SWP_SHOWWINDOW
    );
    mainx();
    flush();
    return 0;

DWORD g_nMainThreadID;

//processing main window messages
long FAR PASCAL WindowProc(HWND   hWnd,UINT   message, WPARAM wParam,LPARAM lParam )

    switch (message)
    
        case WM_PAINT:   flush();
            break;
        case WM_DESTROY: PostQuitMessage(0);
            break;
    
    return DefWindowProc(hWnd, message, wParam, lParam);


int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR  lpCmdLine,  int  nShowCmd )

    (void)hPrevInstance, (void)lpCmdLine;
    WNDCLASS  wc;
    MSG       msg;

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = WindowProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = "Menu_one";
    wc.lpszClassName = "NAME";

    if (!RegisterClass(&wc)) return 0; ;


    //main window
    hwnd = CreateWindow("NAME",    
                        "!", 
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        HWND_DESKTOP, 
                        NULL, 
                        hInstance,
                        NULL
    );
    ScreenMaxX = ::GetSystemMetrics(SM_CXSCREEN);
    ScreenMaxY = ::GetSystemMetrics(SM_CYSCREEN);
    hDC = ::GetDC(hwnd);
    hDCMem  = ::CreateCompatibleDC(hDC);
    hbitmap = ::CreateCompatibleBitmap(hDC, ScreenMaxX, ScreenMaxY );
    ::SelectObject(hDCMem, hbitmap);
    auto hbrush = (HBRUSH)::GetStockObject(WHITE_BRUSH);
    ::SelectObject(hDCMem, hbrush);
    ::PatBlt(hDCMem, 0,0, ScreenMaxX, ScreenMaxY, PATCOPY );
    ::DeleteObject(hbrush);


    CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Th, (LPVOID)hwnd, 0,&g_nMainThreadID);

    ShowWindow(hwnd, nShowCmd);
    UpdateWindow(hwnd);


    while (GetMessage(&msg, NULL, 0, 0))
    
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    
    return msg.wParam;

在 Windows 7 中,条纹的绘制速度与预期相同。但在 Windows XP 中速度不同:

如果我取消注释//brush=::CreatePatternBrush(::CreateBitmap(8, 8, 1, 1, pattern));//flush(); 行,Windows XP 中的绘图速度将相同。为什么这可以解决问题?为什么初始代码的行为在不同版本的 Windows 中会有所不同?

更新

当我在barflush 调用thread_func 之后添加std::cout&lt;&lt;"num = "&lt;&lt;num&lt;&lt;" : bar call\n";std::cout&lt;&lt;"num = "&lt;&lt;num&lt;&lt;" : flush call\n"; 时,输出为

num = 0 : bar call
num = 1 : bar call
num = 1 : flush call
num = 0 : bar call
num = 1 : bar call
num = 1 : flush call
num = 0 : bar call
num = 1 : bar call
num = 1 : flush call
num = 0 : bar call
num = 1 : bar call
num = 1 : flush call
...

顺序似乎是正确的,但是在刷新调用之后没有立即绘制左侧条纹。

【问题讨论】:

如果线程没有得到相同数量的处理器爱,那么线程就会落后。如果总是在超线程内核上运行,那就太糟糕了。测量实际经过的时间以确定要绘制多少,不要只假设 40。GetTickCount() 完成工作。现在,调度、核心性能、Sleep() 的实际数量以及您创建的画笔都不再重要了。 @HansPassant 如果一个线程具有更高的优先级,那么它将在turnstile1 上等待其他线程。我尝试在flush 调用和坐标更改后向控制台打印一条消息,这似乎是正确的,但屏幕上的图像并未反映这一点。我什至在单核 CPU 上也运行过这个程序。 是的,您也可以删除该旋转栅门代码,少一件事要调试。 @HansPassant 抱歉,我不能使用你的方法(老实说我没明白),因为真正的代码要复杂得多,而且它是一种教育任务。 我的意思是渲染层发生了很大变化,因此行为上的差异可能与线程的使用无关。此外,虽然 GDI 在 XP 和 7 上批处理,但实际上这在 7 上似乎不再是什么大问题。尝试调用 GdiSetBatchLimit 将限制设置为 1。 【参考方案1】:

从我的 cmets 中提取一些信息。

请注意,GDI 批处理操作可能会在较慢的机器上导致突发渲染。虽然 GDI 在 XP 和 7 上批处理,但实际上这在 7 上似乎不再是什么大问题。尝试调用 GdiSetBatchLimit 将限制设置为 1。这将导致 GDI 在每次调用后刷新,这可能整体速度较慢,但​​应消除突发行为。

【讨论】:

以上是关于Windows XP 和 Windows 7 中的不同程序行为的主要内容,如果未能解决你的问题,请参考以下文章

Windows 7和Windows XP中的TextBox之间是否存在根本差异

VB.net 中的 IP 地址查找(XP 与 Windows 7)

Windows 7 与 Windows XP 上 Firefox 中的字体行间距

windows xp下的MySQL出错

在 win xp Vista 和 Windows 7 上保存程序数据的位置

Windows 7 XP 模式和 Visual Studio 2003