プログラムを書こう!

実務や自作アプリ開発で習得した役に立つソフトウェア技術情報を発信するブログ

MFCでスレッド処理を行う。

この記事は2018年11月27日に投稿しました。

f:id:paveway:20190914064630j:plain

目次

  1. はじめに
  2. MFCでスレッド処理を行う
  3. おわりに

1. はじめに

こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回は業務で使用しているMFCでスレッド処理を行う方法についてです。

目次へ

2. MFCでスレッド処理を行う

MFCでスレッド処理を行うには、以下の実装例のように行います。

ポイントは2箇所あります。

  1. スレッドで実行する関数にクラスの通常のメンバ関数を指定できません。
    そのためスレッドで実行する関数はstatic関数を指定します。
    その際パラメータに自分自身のクラスを設定しておきます。
    そして起動したstatic関数から、設定したパラメータを利用して通常のメンバ関数を実行します。
    そうすることによって、クラスのメンバ変数、その他のメンバ関数にアクセスできるようになります。
  2. スレッドを停止する場合、呼び出し側はスレッドを処理する関数がいつ終わるかわかりません。
    そのためスレッドを処理する関数が終わる際、メッセージを送信して呼び出し側にスレッドが終了したことを通知します。

実装例

通信の受信処理を想定しています。

CComDialog.h

class CComDialog : public CDialogEx
{
private:
    CWinThread* m_pReadThread; // リードスレッドオブジェクト
    BOOL m_bIsReadTerminate;   // リードスレッド停止フラグ

protected:
    // ダイアログの初期化時に呼び出されます。
    virtual BOOL OnInitDialog();

public:
    // リードスレッド本体を呼び出します(statis関数)
    static UINT CallReadThreadProc(LPVOID pParam);

    // リードスレッド本体
    void ReadThreadProc();

    // 閉じるボタンが押下された時に呼び出されます。
    afx_msg void OnBnClickedBnClose();

    // リードスレッド停止時に呼び出されます。
    afx_msg LRESULT OnReadTerminate(WPARAM wParam, LPARAM lParam);
};
CComDialog.cpp

// リードスレッド停止用のメッセージID
#define WM_USER_READ_TERMINATE (WM_USER + 100)

/**
 * リードスレッドを呼び出だします。
 * static関数
 *
 * @param pParam: AfxBeginThreadの第2引数で指定したパラメータ
 *                今回は自分自身のクラス(CComDialog)
 */
UINT CComDialog::CallReadThreadProc(LPVOID pParam)
{
    // 自分自身のクラスにキャストします。
    CComDialog* pComDiaiog = dynamic_cast<CComDialog*>(reinterpret_cast<CWnd*>(pParam));
    
    // 自分自身のクラスにキャストできた場合
    if (pComDiaiog)
    {
        // 自分自身のクラスのメソッドを呼び出します。
        pComDiaiog->ReadThreadProc();
    }
    return 0;
}

/**
 * リードスレッドを開始します。
 */
void CComDialog::StartReadThread()
{
    // リードスレッドを生成します。
    // AfxBeginThread()の第1引数にスレッドで実行する関数を指定し、第2引数に自分自身のクラスを設定します。
    // そうするとスレッドで実行する関数(CallReadThreadProc)の引数(LPVOID pParam)に、自分自身のクラスが渡されます。
    m_pReadThread = AfxBeginThread(CallReadThreadProc, (LPVOID)this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED , NULL);
    
    // リードスレッドが生成できた場合
    if (m_pReadThread)
    {
        // メインウィンドウに自分自身を設定します。
        m_pReadThread->m_pMainWnd = this;

        // スレッド終了時にオブジェクトを自動的に破棄する設定にします。
        m_pReadThread->m_bAutoDelete = TRUE;
        
        // スレッドを開始します。
        m_pReadThread->ResumeThread();
    }
}

/**
 * リードスレッド本体
 * CallReadThreadProc関数から呼び出されます。
 */
void CComDialog::ReadThreadProc()
{
    // リードスレッド停止フラグが未設定の間繰り返します。
    do
    {
        // 通信処理

        // リードスレッド停止の場合
        if (m_bIsReadTerminate)
        {
            // ループを終了します。
            break;
        }
    }
    while (!m_bIsReadTerminate);

    // リードスレッド停止メッセージを送信し、リードスレッド停止処理を呼び出します。
    this->PostMessage(WM_USER_READ_TERMINATE);
}

BEGIN_MESSAGE_MAP(CComDialog, CDialog)
    ON_BN_CLICKED(IDC_BN_CLOSE, &CXGSettingsDlg::OnBnClickedBnClose)
    
    // リードスレッド停止メッセージ(WM_USER_READ_TERMINATE)が送信された場合、OnReadTerminateを呼び出します。
    ON_MESSAGE(WM_USER_READ_TERMINATE, CComDialog::OnReadTerminate)
END_MESSAGE_MAP()

// メッセージハンドラ

/**
 * ダイアログの初期化の時に呼び出されます。
 */
BOOL CXGSettingsDlg::OnInitDialog()
{
    // スーパークラスの関数を呼び出します。
    CDialogEx::OnInitDialog();

    // リードスレッドを開始します。
    StartReadThread();

    return TRUE;
}

/**
 * 閉じるボタンを押下した時に呼び出されます。
 */
void CXGSettingsDlg::OnBnClickedBnClose()
{
    // リードスレッド停止フラグを設定します。
    m_bIsReadTerminate = TRUE;
}

/**
 * リードスレッド停止時に呼び出されます。
 */
afx_msg LRESULT CComDialog::OnReadTerminate(WPARAM wParam, LPARAM lParam )
{
    // ダイアログを閉じます。
    EndDialog(0);

    return 0;
}

目次へ

3. おわりに

通信を行うダイアログで、通信処理にスレッドを利用した時の実装の一部です。
スレッドの停止をどうやって通知するかわからず、いろいろ調べました。
今回はメッセージを利用した方法にしましたが、他にも方法があるようです。
機会があれば紹介したいと思います。

オンラインで学ぶ!小中学生向けプログラミング D-SCHOOLオンライン

紹介している一部の記事のコードはGitlabで公開しています。
興味のある方は覗いてみてください。

目次へ


私が勤務しているニューラルでは、主に組み込み系ソフトの開発を行っております。
弊社製品のハイブリッドOS Bi-OSは高い技術力を評価されており、特に制御系や通信系を得意としています。
私自身はiOSモバイルアプリウィンドウズアプリを得意としております。
ソフトウェア開発に関して相談などございましたら、お気軽にご連絡ください。

また一緒に働きたい技術者の方も随時募集中です。
興味がありましたらご連絡ください。

EMAIL : info-nr@newral.co.jp / m-futamata@newral.co.jp
TEL : 042-523-3663
FAX : 042-540-1688

目次へ