2017-02-15 38 views
1

此代碼繪製從上面兩個垂直條紋到窗口的底部在兩個線程(對應函數爲每個線程是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中的速度是不同的:

different speed

如果我取消或者//brush=::CreatePatternBrush(::CreateBitmap(8, 8, 1, 1, pattern));//flush();線,在Windows XP中繪製的速度將是相同的。爲什麼這可以解決問題,爲什麼不同版本的Windows中初始代碼的行爲有所不同?

更新

當我後thread_funcbarflush通話添加std::cout<<"num = "<<num<<" : bar call\n";std::cout<<"num = "<<num<<" : 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 
... 

的順序似乎是正確的,但左邊的條紋是不是後立即被吸引同花順呼叫。

+3

如果線程沒有得到愛處理器的相同金額,然後一會落後。如果一個人總是運行在一個超線程的核心上,那就是Bummer。測量實際耗用的時間來計算繪製多少,不要只假設40. GetTickCount()可以完成任務。現在既沒有調度,也沒有核心性能,也沒有實際的Sleep()的數量,也沒有什麼刷你創造的問題了。 –

+0

@HansPassant如果一個線程的優先級更高,那麼它將等待'turnstile1'上的其他線程。我嘗試在'flush'調用並協調更改後向控制檯輸出消息,看起來正確,但屏幕上的圖像沒有反映出這一點。我甚至在單核CPU上運行這個程序。 –

+0

是的,你可以刪除旋轉門代碼,少一件事要調試。 –

回答

1

拉位從我的意見變成一個答案。

注意,GDI批量操作這可能會導致在較慢的機器突發渲染。雖然GDI在XP和7上都批處理,但實際上在7上看起來並不是什麼大問題。嘗試調用GdiSetBatchLimit將限制設置爲1.這會在每次調用後都會導致GDI刷新,這可能會比較慢總體而言,但應該消除突發行爲。