CWinThreadによるスレッド

CWinThreadを継承してスレッドを利用する方法。

CWinThreadの動作

CWinThreadでの実装すべき関数は以下の関数がある。
  1. InitInstance
  2. Run
  3. ExitInstance
実行順序も上記の通り。
終了方法は、自然にスレッドが終了する当然な方法のほかに、中断させる方法もある。
今回の実装では、Run関数をオーバーライドする為、メッセージによる中断が出来ない。
そのため、メンバ変数m_bStopを用意し、さらにセッタとして、SetStop関数を用意する。

CWinThreadを継承したクラスの作成

MFCの ClassWizard を利用して、CWinThread を継承するクラスを作成します。
そして、 InitInstance、 ExitInstance、 Run 関数をそれぞれオーバーライドします。
さらに、先ほどの説明でも登場した、変数m_bStopとSetStop関数を用意する。
CWinThreadを継承したCCustThread(CustThread.h)
#if !defined(AFX_CUSTTHREAD_H__72F1ABDE_7CB7_4B19_908E_AE9ECFFC81CB__INCLUDED_)
#define AFX_CUSTTHREAD_H__72F1ABDE_7CB7_4B19_908E_AE9ECFFC81CB__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define WM_QUITTHREAD WIN_APP + 0x100

/////////////////////////////////////////////////////////////////////////////
// CCustThread スレッド

class CCustThread : public CWinThread{
    DECLARE_DYNCREATE(CCustThread)
protected:
    CCustThread();           // 動的生成に使用されるプロテクト コンストラクタ

// アトリビュート
public:

// オペレーション
public:
    void SetStop();
    
// オーバーライド
    // ClassWizard は仮想関数のオーバーライドを生成します。
    //{{AFX_VIRTUAL(CCustThread)
    public:
    virtual BOOL InitInstance();
    virtual int ExitInstance();
    virtual int Run();
    //}}AFX_VIRTUAL

// インプリメンテーション
protected:
    virtual ~CCustThread();

    // 生成されたメッセージ マップ関数
    //{{AFX_MSG(CCustThread)
        // メモ - ClassWizard はこの位置にメンバ関数を追加または削除します。
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()

private:
    BOOL m_bStop;

};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ は前行の直前に追加の宣言を挿入します。

#endif // !defined(AFX_CUSTTHREAD_H__72F1ABDE_7CB7_4B19_908E_AE9ECFFC81CB__INCLUDED_)
CWinThreadを継承したCCustThread(CustThread.cpp)
#include "stdafx.h"
#include "CThreadSample.h"
#include "CustThread.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CCustThread

IMPLEMENT_DYNCREATE(CCustThread, CWinThread)

CCustThread::CCustThread(){
}

CCustThread::~CCustThread(){
}

BOOL CCustThread::InitInstance(){
    return TRUE;
}

int CCustThread::ExitInstance(){
    return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CCustThread, CWinThread)
    //{{AFX_MSG_MAP(CCustThread)
        // メモ - ClassWizard はこの位置にマッピング用のマクロを追加します。
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCustThread メッセージ ハンドラ
int CCustThread::Run(){
    while(m_bStop){
        ・・・なにか処理・・・
    }
    return 0;
}

void CJPLoadMainThread::SetStop(){
    m_bStop = TRUE;
}

中断/終了処理

終了、中断は明示的に終了することを宣言しなければなりません。
特にマルチスレッドの場合、終了方法を間違えるとメモリリークの原因になります。

スレッドの中断を行なう場合は 外部からSetStop 関数を呼んでもらいます。
SetStop関数を読んでもらうことで、run内のwhileの条件が偽となり、ループが終わります。

CCustThreadクラスのインスタンス化

MFCの ClassWizard を利用すると、コンストラクタが protected で宣言されます。
この場合、通常の宣言ではオブジェクトを生成できません。
CWinThread を継承したクラス(サンプルで言えば CCustThread )をインスタンス化するには、RUNTIME_CLASS 関数 を利用します。
RUNTIME_CLASS 関数 は CRuntimeClass を返します。
返された CRuntimeClass を利用して、 CCustThread をインスタンス化します。
取得した、 CCustThread のインスタンスの CreateThread 関数 を呼び出し、スレッドを開始します。
    CRuntimeClass *pRuntime = RUNTIME_CLASS(CCustThread);
    CCustThread *pThread = (CCustThread*)pRuntime->CreateObject();
    pThread->CreateThread(0, 0, NULL);
ただし、WaitForSingleObject APIを使って、スレッドの監視を行う場合は、CWinThreadクラスのメンバ変数 m_bAutoDelete を FALSEにしておく のが望ましい。
これは、m_bAutoDeleteがTRUEの場合、スレッドの処理が終了すると、自動的にCWinThreadオブジェクトが削除される為です。
スレッド終了をWaitForSingleObject APIで監視していると、終了後にハンドルを確認したくても無効なハンドルになります。
特に以下の場合にもんだがあります。
 CWinThread *m_pThread;
 
    ・・・・
 
 if(::WaitForSingleObject(m_pThread->m_hThread)){
    ・・・・
 }
自動的にCWinThreadオブジェクトが削除される為、終了後に上記の参照を行うと、削除された領域を参照してしまう為、最悪の場合、アプリケーションエラーが発生します。

スレッドを強制的に破棄させる

どうしても削除しなければならない時は、
 CWinThread *m_pThread;
 
    ・・・・
    if(m_pThread->SuspendThread() != 0xFFFFFFFF){
        delete m_pThread;
    }
    ・・・・
 
とすればよい。
ただし、これはかなり強引な方法になる為、お勧めは出来ない。
ここで、SuspendThread()関数を呼び出しているが、SuspendThread()関数で一時停止しておかないと、 delete演算してポインタを解放した後に、Run()関数が呼び出されることが頻繁に発生した。
そのため、アプリケーションエラーが発生することがあった。
スレッドを1度止めて、その後にdelete演算子で削除すると、Run()関数が呼び出されない。
そのため、アプリケーションエラーが発生しなくなる。

最後に

この実装が一般的な実装かどうかは分かりません。
(少なくとも、MSDNではRunをオーバーライドすること自体が特殊なケースっぽく書いてあるので一般的ではないかもしれません。)
自分なりに調べて、最適な実装を行なうことをお勧めします。
なお、ここでは説明しませんでしたが、AfxBeginThreadを利用してもスレッドを生成できます。