2017-02-15 31 views
1

このコードでは、ウィンドウの上端から下端までの2つの垂直ストライプが2つのスレッドで描画されます(各スレッドの対応する関数はthread_funcです)。最初のスレッドは左側のストライプの一部を描画し、次に2番目のスレッドは右側のスレッドの一部を描画し、次に1番目のスレッドを再度描画します。セマフォとクリティカルセクションは、この順序を保証するために使用されます。Windows XPおよびWindows 7でのプログラムの動作が異なります。

#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();行のコメントを解除した場合

different speed

、Windows XPでの描画の速度が同じになります。これにより問題が解決するのはなぜですか、異なるバージョンのWindowsではinititalコードの動作が異なるのはなぜですか?

更新

私はthread_funcstd::cout<<"num = "<<num<<" : bar call\n";std::cout<<"num = "<<num<<" : flush call\n";bar後とflushの呼び出しを追加すると、出力は

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

スレッドが同じ量のプロセッサを使用していない場合、1つは遅れます。ハイパースレッドのコアで常に実行されている場合は、バマー。 GetTickCount()は、実際の経過時間を計測して描画する量を把握し、40と仮定しないでください。スケジューリングもコアパフォーマンスも、Sleep()の実際の量も、あなたが作成するブラシももう問題になりません。 –

+0

@ HansPassantあるスレッドの優先度が高い場合は、 'turnstile1'で他のスレッドを待つことになります。私は 'flush'呼び出しと座標の変更の後にコンソールにメッセージを出力しようとしましたが、正しいと思われますが、画面上のイメージには反映されません。そして、私はこのプログラムをシングルコアCPUでも実行しました。 –

+0

はい、あなたはそのターンスタイルコードを削除することができます。 –

答えて

1

私のコメントから回答にビットを引っ張ります。

GDIは、遅いマシンでバースト的なレンダリングを引き起こす可能性がある操作をバッチ処理することに注意してください。 GDIはXPと7の両方でバッチ処理を行いますが、実際にはこれ以上問題にはならないようです。GdiSetBatchLimitに電話をかけて制限を1に設定してください。これにより、GDIが呼び出されるたびにフラッシュされます。バースト的な振る舞いを排除すべきである。

関連する問題