CAD日記

主にAutoCADのことについて書いているけど、近頃は投資系ネタに注力している。自動売買、仮想通貨、PC関係、プログラミングなど。@caddiary

ソフト開発

マルチスレッドプログラミング(C++編)

投稿日:2018年7月28日 更新日:

マルチスレッドプログラミング(VB.net編)
マルチスレッドプログラミング(C#編)
もあるので、ご覧あれ。

5秒以上の時間を要する処理をやらせていると応答なしになってかっこ悪い、なんてことをアラフィフ元プログラマーのおいらが言われて、それはそうですよねぇなんて言いつつ、どうしたらいいのかと周囲の人間に聞くも大半の人間はわからず、そのあたりに詳しい人間に聞いたらマルチスレッドと非同期処理の講義を20分も聞かされてしまった。案件がVB.netということもあり、自分で組めるものではなかった。組めないなりにググって要点は把握できた。最終的には、協力会社の何でも書けちゃうプログラマに依頼して、VB.net案件は片が付いた。昔ながらのやり口であるThreadクラスを使う方法で落ち着いたが、過程としてasync/awaitなる最新の非同期処理を案内されるもおいらがさっぱりわからんかったので、おいらが別の人間に説明できるものでもなくあきらめた。

そんな他人依存でプログラム案件をやり過ごしたことが、無性に腹立たしかったため、土曜のくそ朝っぱらからプログラミングに取り組んだ。おいらが書けるのはC++だけなもんだから、近頃の風潮とは合致しないんだけれども、ともあれ書いてみることは重要だ。AfxBeginThreadというキーワードは覚えていたし、昔取った杵柄でもあるからとガッツリ書いてみたなり。

要件は以下の通り。時間がかかる処理をしているあいだに、画面上には過程を表す表示を行い、中止ボタンにより処理を止めることができる。中止ボタンを押すことで、本当に止めてもいいかとメッセージを表示し、メッセージ表示中は処理を一時中断する。もちろん、処理に5秒以上かかっても応答なしにならない。UI設計は以下の通り。

ソースコードを以下に抜き出してみる。ソースだけでは使い勝手悪いだろうから、プロジェクトそのものをここに上げておく。VisualStudio 2010 C++で作ったもの。わかりそうでわからないマルチスレッドについて、いますぐ実装したい人は持っていってくれ。

BOOL CThreadTestDlg::OnInitDialog()
{
  CDialogEx::OnInitDialog();

  // このダイアログのアイコンを設定します。アプリケーションのメイン ウィンドウがダイアログでない場合、
  //  Framework は、この設定を自動的に行います。
  SetIcon(m_hIcon, TRUE);      // 大きいアイコンの設定
  SetIcon(m_hIcon, FALSE);    // 小さいアイコンの設定

  UpdateMsg( 0 );  // メッセージ初期化
  GetDlgItem(IDC_BUTTON2)->EnableWindow(0);  // 中止ボタンを無効化
  m_stcResult.SetWindowText(_T(""));  // 結果文字列をクリア

  return TRUE;
}

// 開始ボタン押下
void CThreadTestDlg::OnBnClickedButton1()
{
  GetDlgItem(IDC_BUTTON1)->EnableWindow(0);  // 開始ボタンを無効化
  GetDlgItem(IDC_BUTTON2)->EnableWindow(1);  // 中止ボタンを有効化
  GetSystemMenu(FALSE)->EnableMenuItem( SC_CLOSE, MF_GRAYED );  // ×ボタンを無効化
  m_stcResult.SetWindowText( _T("") );  // 結果文字列をクリア
  UpdateMsg( 0 );  // メッセージ初期化

  // サブスレッド開始
  m_pThread = AfxBeginThread( (AFX_THREADPROC)ExecProc, (LPVOID)this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
  m_pThread->m_bAutoDelete = FALSE;  // 自動破棄フラグクリア
  m_pThread->ResumeThread();    // サスペンド解除
}

static UINT ExecProc( LPVOID pParam )
{
  CThreadTestDlg* pDlg = (CThreadTestDlg*)pParam;
  pDlg->ExecFunc();
  return 0;
}

void CThreadTestDlg::ExecFunc()
{
  // この関数の中がサブスレッド。サブスレッドからやってはいけないこと。
  // ・画面周りのコントロール(メインスレッド)を変更する
  // ・メインスレッドから変更される可能性がある変数を変更する
  // ようするに、メインとサブでは一定の独立性を保つべしということ
  try
  {
    for( int i=0; i<10; i++ )
    {
      Sleep(1000); // 時間がかかる処理
      if( m_Stop )  // 中止されたフラグを見て、ループを抜ける
        break;
      UpdateMsg( i+1 );
    }
    if( m_Stop )
      m_strResult = _T("中止された。");  // ここはメンバー変数を使わずに、メッセージパラメータで渡したほうがいいのかも。。
    else
      m_strResult = _T("最後まで実行された。");
  }
  catch(...)
  {
    m_strResult = _T("異常終了。");
  }
  PostMessage( WM_APP_THREADEND, 0, 0 );  // サブスレッドが終わったことをメインスレッドに知らせる
}

// サブスレッドが終わった
LRESULT CThreadTestDlg::OnThreadEnd(WPARAM wParam, LPARAM lParam)
{
  if( !m_pThread )  // サブスレッドがスタートしていない(念のためチェック)
    return 0L;
  WaitForSingleObject( m_pThread->m_hThread, INFINITE );  // サブスレッドが終了するのを待つ
  delete m_pThread;
  m_pThread = NULL;

  GetDlgItem(IDC_BUTTON1)->EnableWindow(1);  // 開始ボタンを有効化
  GetDlgItem(IDC_BUTTON2)->EnableWindow(0);  // 中止ボタンを無効化
  GetSystemMenu(FALSE)->EnableMenuItem( SC_CLOSE, MF_ENABLED );  // ×ボタンを有効化
  m_stcResult.SetWindowText( m_strResult );  // 結果を更新
  m_Stop = FALSE;

  return 0L;
}

// 中止ボタン押下
void CThreadTestDlg::OnBnClickedButton2()
{
  if( !m_pThread )  // サブスレッドがスタートしていない(念のためチェック)
    return;
  m_pThread->SuspendThread();  // サブスレッド中断
  if( MessageBox(_T("中止しますか?(メッセージ表示中はスレッド中断)"), _T("確認"), MB_YESNO ) != IDYES )
  {
    m_pThread->ResumeThread();  // サブスレッド再開
    return;
  }
  m_pThread->ResumeThread();  // サブスレッド再開
  m_Stop = TRUE;  // 中止されたフラグを立てる(サブスレッド側が検知する)
  UpdateMsg( 0 );  // メッセージ初期化
}

void CThreadTestDlg::UpdateMsg( int counter )
{
  CString msg;
  msg.Format( _T("時間がかかる処理を実行中 %d/10"), counter );
  m_stcMsg.SetWindowText( msg );
}

やってみると、やっぱプログラムはおもしろいなと思う。仕事でプログラムをしなくなってからというものその喜びを忘れてしまい、やれ設計書だ工程表だ、打ち合わせだとつまらんことしてるなぁ。とは言え、昔みたいに頭がさえて根気がある状況でもないので、そんなかったりー仕事のほうがいいのかもしれんな、アラフィフオヤジにとってみればさ。近い将来、C#でasync、awaitの非同期処理にアプローチしてみて、同様のサンプルを作ってここで公開してみましょかね。

-ソフト開発

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

関連記事

no image

プログラムっておもしろいなぁとつくづく思うなり

ここんところぜんぜんプログラムを書いてなくて仕事がつまんなくなってきたところに、トラブル対応があってプログラムをじっくりと書いてみた。いやはやおもしろい。余計な仕事が飛び込んでこない環境で、スピッツ聞 …

no image

マルチスレッド

ついに出来ました。 マルチスレッド技術を利用した画期的機能が。 って、そんな大したものではありません。 スレッド化技術は、この時代当たり前になりつつありますが、 なかなか難しそうで、手をつけてませんで …

no image

ベクタープロレジ大賞

Vectorがやってる、もっとも人気のあったダウンロードソフトを決める賞です。 Vectorと言えば、フリーウェアやシェアウェアをダウンロードできるところで一番有名なところです。窓の杜というところもあ …

no image

簡単インストーラ

インストーラと言えば、Install Shield です。最新バージョンは11.0で価格は44万1千円。高いんです。高機能版(Premier Edition)で、多国語(全33ヶ国語)対応という代物だ …

若いもんにC++を教えてやって、これまでやってきたことが少しでも伝わってくれればと願う夜

ファイルをセレクトするというC++で作ったActiveXのDLLのデバッグができないかと彼がおいらのところに来た時点で、そいつはなかなかチカラがあるなと感じた。