■プロフィール

うなちょ

Author:うなちょ
うなちょのほんわか日記へようこそ

RagnarokOnlineと、PC版Minecraft++Forge+MODで
遊びつつ、片手間でWindowsのソフト作って遊んでます

■最近の記事
■最近のコメント
■最近のトラックバック

■月別アーカイブ
■カテゴリー

■リンク
■RSSフィード
■ブログ内検索

うなちょホットライン

うなちょへ直接連絡したい場合は、こちらから。
うなちょのPCメールへ着信するので、比較的
レスポンス早いかも…?(5分周期でチェック中)
※E-Mailですので、メルアドがある方のみです
※うなちょの携帯に転送する方法は、直接聞いて
  くだし。
無料アクセス解析
スポンサーサイト
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。


スポンサー広告 | --:--:--

ワーカースレッド書き書き
久々にMFC書き書き。

最近、よくMFCのウィンドウクラス(CDialogがメインだけど)でワーカースレッドを
使うので、メモ代わりに書いておこう。


・ウィンドウ(クラス)に所属するワーカースレッドの手抜きメンバアクセス
 ワーカースレッドは、static宣言するのでそのままではクラスメンバにアクセスできない。
 簡単に言うと、thisポインタが無い。
 スレッドパラメータでthisを渡して、参照ポインタを付ければアクセスできるのだが、
 さすがに面倒。

 最近は、ワーカースレッドから呼ばれる処理を追加して、そこの中にメインの処理を書いている。

 

 class CWorkerTestDlg : public CDialog
 {
 public:
   CWorkerTestDlg();
   virtual ~CWorkerTestDlg();

 protected:
   virtual BOOL OnInitDialog();
   afx_msg void OnDestroy();
   afx_msg void OnPaint();
   afx_msg void OnTimer( UINT_PTR nIDEvent );

 private:
   BOOL m_fAbortThread;
   CWinThread* m_thWorkerThread;
   UINT WorkerExecute();
   static UINT WorkerThread( void* pParam );
   BOOL BeginWorkerThread();
 };

 UINT CWorkerTestDlg::WorkerExecute()
 {
   while( m_fAbortThread == FALSE ){
     処理ごにょごにょ
   }

   return( 0 );
 }

 UINT CWorkerTestDlg::WorkerThread( void* pParam )
 {
   CWorkerTestDlg* pDlg = (CWorkerTestDlg *)pParam;

   UINT uiResult = pDlg->WorkerExecute();
   pDlg->m_thWorkerThread = NULL;

   return( uiResult );
 }

 BOOL CWorkerTestDlg::BeginThread()
 {
   m_fAbortThread = FALSE;
   m_thWorkerThread = AfxBeginThread( WorkerThread, this );

   return( m_thWorkerThread != NULL ? TRUE : FALSE );
 }


 上記処理では、「WorkerExecute()」が、手抜きワーカースレッド。
 処理自体は、WorkerThread()のタイムスライスで動いてるけど、「pDlg->WorkerExecute()」
 としている為、WorkerExecute()はthisポインタがあり、WorkerThread()のpDlgポインタが、
 WorkerExecute()ではthisポインタとなる。

 この記述をし始めてから、大分楽になった気もするが、どうなんだろうなぁ…?

 ワーカースレッドの起動は、上のBeginThread()を呼ぶ。
 まぁ、こんな感じで起動するよ、というだけ。


・ワーカースレッドの中断と監視方法
 VB6の頃は、ボタンを押すとか、ウィンドウが移動するとかのイベント処理は、他のイベントと
 排他的に動いていたんだよね。
 この点については、MFCも変わらない。
 イベントで処理していれば、他のイベントが発生しても、メッセージキューに入って処理待ちに
 なるだけ。

 最近、リストボックスを使って動作ログを表示するんだけど、ワーカースレッドの終了指示を
 ボタンで行い、その中でワーカースレッドも参照する終了フラグをTRUEにしてタイムアウトを
 待つ処理にしておいて、「中断しています」「中断が失敗しました」って言うのをログに出そうと
 すると、表示が全て終了した後になってしまっていたのよ。
 なんでだろうなぁと思ったら、ごくごく当たり前だったり。

 ボタンイベントで中断フラグをTRUEにしてワーカースレッドの終了を待っているわけだから、
 そこに動作ログにメッセージを追加しても、ボタン処理が終わるまでは再描画が行われない。
 すでにイベント処理中な訳だしねぇ。

 しょうがないので、いずれかの方法を取る。

 1) タイマで監視
   中断フラグをTRUEにした後で、SetTimer()で100ミリ秒程度の周期で終了を監視する。
   これだと、ボタンを押してフラグセット、SetTimer()した後は処理を終了できるので、
   リストボックスの再描画が行われる。これはよく使う。
   欠点として、面倒。OnTimer()に処理かかなきゃいけないし、タイムアウトを監視する
   カウンタつけないといけないし。

 2) リストボックスへ文字を追加したら、RedrawWindow()
   一番楽。
   リストボックスへの追加は関数化してあって、追加後5120行を越えたら、古い行から
   消していって5120に保ち(5120は適当)、最後にSetTopIndex()で最後の行を表示範囲に
   入る様にスクロールさせている。
   ここにRedrawWindow()を入れれば良い。
   欠点として、常にRedrawWindow()が行われるので、リストボックスが多重更新される
   タイミングもあるという感じ。
   処理時間がシビアになると、もったいない処理時間となる…かも?


・ワーカースレッドの終了と終了監視
 そんなに難しくはないのだが、ワーカースレッドも見る、初期値FALSEのBOOL変数をクラスに
 追加しておいて、ボタンが押されたらこのフラグをTRUEにするだけ。
 あとは、インループしない様にループさせるか、タイマで監視するか。

 簡単な処理だと、こんな感じ。

 //終了ボタン処理
 void CWorkerTestDlg::OnButtonclicked1()
 {
   //終了要求ON
   m_fAbortThread = TRUE;

   //終了待ち。ワーカースレッドが終了するか、約3秒経過でタイムアウトして抜ける
   for( int nTimeout=0; (nTimeout<3000) && (m_thWorkerThread != NULL); nTimeout+=100 ){
     Sleep( 100 );
   }

   //終了要求OFF
   m_fAbortThread = FALSE;

   //終了結果確認
   if( m_thWorkerThread == NULL ){
     //終了成功
   } else {
     //終了失敗
   }
 }

 ワーカースレッド側のコードは、一番上にあるコード参照。
 ワーカースレッドでは、ループの頭や途中で、必ずm_fAbortThread==FALSEであることを監視。
 これがTRUEになったら、ループを全て終了(break)させて、終了させる。
 終了させる際、AfxBeginThread()の返すポインタを記憶している、m_thWorkerThreadにNULLを
 セットして終了することを忘れない様にする。
 フラグで終了指示を出して、本当に終了したかを確認するために、CWinThreadポインタを消す、
 終了側はこれがNULLになるのを確認する、というハンドシェークで終了させる。
 これが一番シンプルな感じがする。
 いくらなんでも、TerminateThread()はしないよ!
 …特殊な事情があって、タイムアウトしたならば別だけど。

 派生として、スレッドラストで終了フラグをFALSEにするという手もあって、nTimeoutのループ
 では、ポインタでなくて終了フラグがTRUEでなくなったことを監視すれば、同様のことが
 可能となる。
 ただし、終了フラグが落ちるのを監視するので、ちょいと後処理が心配になる。
 (一応、for()側の処理の最後にでも、終了フラグを常にFALSEにすればいいんだけど…)


ワーカースレッドを書く際に、真っ先に書いておくのは、こんな所かなぁ。
いろんな手段をやってきたけど、最近記述するこのコードが、一番あっている気がする。


|ω゚)ノ 手抜き万歳!  でもおいら達の料理は手を抜くなよ!(゚ω゚*)ナ?


┣【MFCなこと】 | 23:03:33 | Trackback(0) | Comments(0)

コメントの投稿

管理者にだけ表示を許可する

FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。