none
WindowsVistaでスレッド処理がうまく処理できない。 RRS feed

  • 質問

  • Visal Studio 2010 を使用して、スレッド処理を作ってみましたが、フリーズしてしまいます。

    スレッド処理は以下の通りなのですが、

    // スレッド処理
    DWORD WINAPI MyThread( LPVOID lpData )
    {
     MYDATA* lpMyData;
     HWND hWnd;
     HDC hdc, hdc_cat;
     HBITMAP hBmp, hBmpCat;
     SYSTEMTIME st;
     char szBuf[64];
     DWORD dwStart, dwEnd, dwMSec;
     HFONT hFont;

     // メイン処理からのデータをコピー
     lpMyData = (MYDATA*)lpData;

     // ウィンドウハンドル取得
     hWnd = lpMyData->hWnd;

     // メモリデバイスコンテキスト取得
     lpMyData->hdc_mem = CreateCompatibleDC( NULL );

     // デバイスコンテキスト取得
     hdc = GetDC( hWnd );

     // ビットマップ作成
     hBmp = CreateCompatibleBitmap( hdc, 600, 400 );

     // 画像ロード
     hBmpCat = (HBITMAP)LoadImage( hInst, "MYBMP", IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR );

     // メモリデバイスコンテキスト取得
     hdc_cat = CreateCompatibleDC( NULL );

     // オブジェクトを選択
     SelectObject( hdc_cat, hBmpCat );
     SelectObject( lpMyData->hdc_mem, hBmp );

     // デバイスコンテキスト解放
     ReleaseDC( hWnd, hdc );

     // フォントの作成
     hFont = MyCreateFont( "MS ゴシック", 80 );

     // オブジェクト選択
     SelectObject( lpMyData->hdc_mem, hFont );

     // 文字色設定
     SetTextColor( lpMyData->hdc_mem, RGB( 255, 0, 0 ) );

     // 背景モード設定
     SetBkMode( lpMyData->hdc_mem, TRANSPARENT );

     // 終了フラグが立つまで繰り返し
     while( !lpMyData->bEnd )
     {
      // タイマ分解能を設定
      timeBeginPeriod( 40 );

      // 開始時間取得
      dwStart = timeGetTime();

      // 最小タイマ分解能を解除
      timeEndPeriod( 40 );

      // 矩形塗り潰し
      PatBlt( lpMyData->hdc_mem, 0, 0, 600, 400, WHITENESS );

      // 画像転送
      BitBlt( lpMyData->hdc_mem, 0, 0, 600, 400, hdc_cat, 0, 0, SRCCOPY );

      // システム時刻取得
      GetLocalTime( &st );

      // システム時刻出力
      wsprintf( szBuf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond );
      TextOut( lpMyData->hdc_mem, 10, 10, szBuf, (int)strlen( szBuf ) );

      // 描画指示
      InvalidateRect( hWnd, NULL, FALSE );

      // タイマ分解能を設定
      timeBeginPeriod( 40 );
      
      // 終了時間取得
      dwEnd = timeGetTime();

      // 最小タイマ分解能を解除
      timeEndPeriod( 40 );

      // 処理時間取得
      dwMSec = dwEnd - dwStart;

      // スリープ
      Sleep( 40 - dwMSec );

     }

     // オブジェクト破棄
     DeleteObject( hBmp );
     DeleteObject( hFont );
     DeleteDC( lpMyData->hdc_mem );

     return 0;

    }

    特にループの中のtimeGetTime()でうまく値が取れていないような感じになっています。

    クイックウォッチで見ようとしてもdwStart、dwEnd、dwMSecは「  dwStart CXX0017: エラーです: シンボル "dwStart" が見つかりません 」と

    表示され、中身を見ることができません。Win32プログラミングの経験もあまりないため、どのように対処してよいかわかりません。

    ご教授お願いします。参考までに、OSはWindws Vista Home Premiumです。

     

    2010年8月27日 15:33

回答

  • 補足です。

     

    GDIはThread Safeではありません。

     

    Sleepによる同期の取り方も、

    Sub ThreadでのGetDCによるWindowのDC取得も、

    Window側Threadの処理内容によっては、不安が残ります。

     

    Window側のThreadでlpMyData->hdc_memの内容を

    BitBltしていませんか?

     

    その場合、OSの描画要求によるWM_PAINT処理時と、

    Sub ThreadのlpMyData->hdc_memの処理がかち合った場合に、

    意図しない結果になる可能性があります。

     

    上記を配慮されていない場合、例えば以下のような考え方があります。

      1.DC/Bitmapの下準備と解放は、Windowが属するThreadで行うようにして、

        Sub Threadでは、描画処理を中心に行う。

      2.Sub Threadで描画が終わったらGdiFlushを呼んでおく。

      3.Windowの描画が終わるまで待つ必要があるなら、

         InvalidateRectの代わりにRedrawWindowを呼ぶ。

      4.lpMyData->hdc_memへの排他アクセスはMutexで行う。

        これはWindowが属するThreadでも同じです。

      5.While内の処理が常に回りっぱなしになるのを防ぐため、

        Sleepで適当に待たせる。

      6.DC/Bitmapの解放はSub Threadの終了を待つ必要があるので、

        WaitForSingleObject で終了を待つ。

     

    上記は大まかな例ですし、

    手段は要件により様々ですので、知識を十分に蓄えた上で最適な手段を考察ください。

     

     

    [Using Mutex Objects]

      http://msdn.microsoft.com/en-us/library/ms686927(VS.85).aspx

     

    [Waiting for Multiple Objects]

      http://msdn.microsoft.com/en-us/library/ms687055(v=VS.85).aspx

     

    [Synchronous and Asynchronous Drawing]

      http://msdn.microsoft.com/en-us/library/dd145126(VS.85).aspx

     

    [GdiFlush Function]

      http://msdn.microsoft.com/en-us/library/dd144844(VS.85).aspx

     

    [RedrawWindow]

      http://msdn.microsoft.com/en-us/library/dd162911(v=VS.85).aspx

    • 回答の候補に設定 山本春海 2010年9月3日 4:50
    • 回答としてマーク 山本春海 2010年9月15日 2:45
    2010年8月27日 18:32

すべての返信

  • >Sleep( 40 - dwMSec );

    計算結果が負になっていませんか?

     

    負になっているとSleepの引数はDWORD型のため、

    しばらくSleepから復帰しませんよ。

    OutputDebugStringで値を出力し、確認してみてください。

     

    [OutputDebugString]

      http://msdn.microsoft.com/ja-jp/library/cc428973.aspx

     

    或いは、下記手段でも変数の中身を確認できるでしょう。

      1.フリーズしている状況で全てブレークする

      2.スレッドのTabから該当スレッドを選択する。

      3.コールスタックから MyThread 関数まで遡る

     

    まず、上記を確認してみてください。

    2010年8月27日 16:10
  • 補足です。

     

    GDIはThread Safeではありません。

     

    Sleepによる同期の取り方も、

    Sub ThreadでのGetDCによるWindowのDC取得も、

    Window側Threadの処理内容によっては、不安が残ります。

     

    Window側のThreadでlpMyData->hdc_memの内容を

    BitBltしていませんか?

     

    その場合、OSの描画要求によるWM_PAINT処理時と、

    Sub ThreadのlpMyData->hdc_memの処理がかち合った場合に、

    意図しない結果になる可能性があります。

     

    上記を配慮されていない場合、例えば以下のような考え方があります。

      1.DC/Bitmapの下準備と解放は、Windowが属するThreadで行うようにして、

        Sub Threadでは、描画処理を中心に行う。

      2.Sub Threadで描画が終わったらGdiFlushを呼んでおく。

      3.Windowの描画が終わるまで待つ必要があるなら、

         InvalidateRectの代わりにRedrawWindowを呼ぶ。

      4.lpMyData->hdc_memへの排他アクセスはMutexで行う。

        これはWindowが属するThreadでも同じです。

      5.While内の処理が常に回りっぱなしになるのを防ぐため、

        Sleepで適当に待たせる。

      6.DC/Bitmapの解放はSub Threadの終了を待つ必要があるので、

        WaitForSingleObject で終了を待つ。

     

    上記は大まかな例ですし、

    手段は要件により様々ですので、知識を十分に蓄えた上で最適な手段を考察ください。

     

     

    [Using Mutex Objects]

      http://msdn.microsoft.com/en-us/library/ms686927(VS.85).aspx

     

    [Waiting for Multiple Objects]

      http://msdn.microsoft.com/en-us/library/ms687055(v=VS.85).aspx

     

    [Synchronous and Asynchronous Drawing]

      http://msdn.microsoft.com/en-us/library/dd145126(VS.85).aspx

     

    [GdiFlush Function]

      http://msdn.microsoft.com/en-us/library/dd144844(VS.85).aspx

     

    [RedrawWindow]

      http://msdn.microsoft.com/en-us/library/dd162911(v=VS.85).aspx

    • 回答の候補に設定 山本春海 2010年9月3日 4:50
    • 回答としてマーク 山本春海 2010年9月15日 2:45
    2010年8月27日 18:32
  • Sleep( 40 - dwMSec );ですが、

    OutputDebugStringで値を出力して確認してみましたが、確かに負の数になっていました。

    しかし、dwEnd-dwStartの値が大きな値となってしまっています。大体この差の値が

    8500~10000の範囲で帰ってきておりミリ秒なので、8.5秒~10秒くらいだと思うのですが、

    実際それほど長い処理はしていません。見た目は一瞬の処理なのですが、なぜこれほどの

    大きな値の差となるのかが分かりません。またよろしくお願いします。

    2010年8月27日 18:47
  • >実際それほど長い処理はしていません。

    各関数の呼び出しで、どの程度時間がかかっているか計測してみてください。

    2010年8月27日 19:08