マルチスレッドで描画する方法について

[戻る]
mAro 2002/11/23(土) 02:30:25
mAroです。

MFCを使ったMDIプログラムを書いています。
親ウィンドウと子ウィンドウで描画を行う際、
親の方の描画に非常に時間がかかる処理をしているため
子の方の描画がかなり遅れてしまい、
マルチスレッドで描画させようと思いましたが、詰まってしまいました。

CDC *tmp  // スレッドで参照する方法がわかりませんでした。
class ParentView : public CView
class ChildView : public CScrollView

UINT ThreadDraw(LPVOID pParam){
  // 描画関数の宣言省略
  tmp->MoveTo(X1,Y1);
  tmp->LineTo(X2,Y2);
}
void ParentView::OnDraw(CDC *pDC){
  tmp=pDC;
  AfxBeginThread(ThreadDraw,this);
}
void ChildView::OnDraw(CDC *pDC){
  // 描画関数の宣言省略
  pDC->MoveTo(X1,Y1);
  pDC->LineTo(X2,Y2);
}

2つのビューの関係はこんな感じになっています。
親ウィンドウの描画でいつもプログラムが落ちてしまいます。
デバッグモードでtmpの中身を見たところ、ハンドルが「???」になっていました。
(たまに何かの拍子にハンドルが入ってることもあるが結局は落ちる)

マルチスレッドプログラミングは初めてでデバッグ方法もわからず
どうしようもない状態なので、ご教授願います。

かんな 2002/11/23(土) 02:50:11
MFCオブジェクトはスレッドセーフにできてないので
オブジェクトを渡すよりハンドルを渡した方がいいと思います。

あと、OnDrawから抜けるとpDCは無効になります。
MFCソースのOnDrawを呼び出すところを見ればわかります。

mAro 2002/12/03(火) 16:20:04
mAroです。
返信遅れてすいません。

CDC *tmp; 
class ParentView : public CView
class ChildView : public CScrollView

void ParentView::MyDraw(CDC *pDC){
  pDC->MoveTo(X1,Y1);
  pDC->LineTo(X2,Y2);
}
UINT ThreadDraw(LPVOID pParam){
  // 描画関数の宣言省略
  ParentView *pView = (ParentView*)pParam;
  pView->MyDraw(tmp);
  return 0;
}
void ParentView::OnDraw(CDC *pDC){
  tmp=pDC;
  AfxBeginThread(ThreadDraw,this);
}
void ChildView::OnDraw(CDC *pDC){
  // 描画関数の宣言省略
  pDC->MoveTo(X1,Y1);
  pDC->LineTo(X2,Y2);
}

このようにプログラムを書き換えたことにより、
ハンドルはきちんと戻るようになりました。(デバッグ時で確認する限りは…)
しかしやはり親ウィンドウの描画の途中で落ちてしまいます。
デバッグで1行ずつ描画させていくとなぜか無事に描画できます。
MFCでマルチスレッドを使うこと自体、難しいのでしょうか。

渋木宏明(ひどり) [E-Mail] [HomePage] 2002/12/03(火) 18:11:26
>しかしやはり親ウィンドウの描画の途中で落ちてしまいます。

同一のDCへの同時アクセスは避けた方がいいと思いますよ。
デバッグもしづらいんで、ハマると思います。

(1) 各スレッドにはメモリDCを渡し、そこに描画させる。
(2) 描画処理が完了したらプライマリスレッドに通知を行う。
(3) プライマリスレッドで仮想画面を実画面に BitBlt() する。

なんて手順にした方が面倒ごとが避けられると思います。

mAro 2002/12/13(金) 02:18:11
仮想画面に書き込むために
CDC *pVDC;
pVDC=(CDC*)malloc(sizeof(CDC));
と記述して、これに描画し、
OnDraw()中で
pDC->BitBlt(0,0,640,280,pVDC,0,0,SRCCOPY);
としたのですが、描画されませんでした。
(0,0,640,280)は親ウィンドウの領域です。
また子ウィンドウの描画途中で落ちてしまいます。

いぷ 2002/12/13(金) 09:33:57
>pVDC=(CDC*)malloc(sizeof(CDC));

コンストラクタが走らないので正常に動きません。
newを使ってください。

mAro 2002/12/17(火) 02:13:51
>コンストラクタが走らないので正常に動きません。
>newを使ってください。

pVDC = new CDC();
としたところ、

CPen newPen ;
CPen *oldPen ;
newPen.CreatePen( PS_SOLID, 1, RGB( 0, 0, 255 ) ) ;
oldPen = pVDC->SelectObject( &newPen ) ;

の pVDC->SelectObject(&newPen) で落ちてしまいます。
具体的には
CDC::SelectObject関数内の
ASSERT(m_hDC != NULL) で落ちます。

BQ 2002/12/20(金) 14:07:16
new CDC() としただけでデバイスコンテキストは作成されていません。
CDC::CreateCompatibleDC()
などを用いてデバイスコンテキストを初期化する必要があります。

このとき結びついたデバイスコンテキストリソースハンドルが CDC::m_hDC に保存されます。
初期化が行なわれていないと m_hDC が NULL のままなので、
ASSERT(m_hDC != NULL);
で落ちることになります。

古いスレッドには追加発言できません。