Assertion Failed! -15ページ目

[MFC]マルチスレッド2

マルチスレッドその2


前回 はマルチスレッドでは、スレッド終了前にメイン処理が終了するとメモリリークが

発生するところまで。


で、これを解消するためには、メイン処理が終了する前にスレッド処理が終了するのを

待つ(同期する)必要がある。


// スレッド関数

UINT ThreadFunc(LPVOID param)
{
  printf("スレッド起動\n");

  Sleep(1000);

  printf("スレッド終了\n");
  return 0;
}

// メイン処理

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
  int nRetCode = 0;
  printf("メイン開始\n");


  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
  {
    ・・・中略

  }
  else
  {
    CWinThread *pThread = AfxBeginThread(ThreadFunc, NULL);


    if (::WaitForSingleObject(pThread->m_hThread, INFINITE) == WAIT_FAILED)
    {
      printf("wait error\n");
    }

  }
  printf("メイン終了\n");
  return nRetCode;
}


スレッドの終了を同期するために、WaitForSingleObject関数を使う。

コレは、第一引数にスレッドのハンドルを指定し、第二引数にタイムアウト時間

設定することで、指定時間指定スレッドの終了を待つものである。


第一引数のスレッドハンドルは、スレッド生成時に受け取ったCWinThread

のメンバ変数 m_hThread を指定すればよい。


第二引数にINFINITEを指定すると、タイムアウト時間は無制限つまり

スレッドが終了するまで待ち続けるということになる。


実際に実行した際の結果はこちら。


[結果]

> メイン開始

> スレッド起動

> スレッド終了

> メイン終了


今回はきちんとスレッドの起動と終了がメインの間に入っている。

通常であれば、コンソール画面も一瞬で消えてしまう程度のコードだが、

スレッド関数内の1秒スリープがあるため、コンソールも1秒ちょっと表示される。

また、メモリリークも解消された。


しかし、今度は次のような問題が出てくる。


WaitForSingleObject で待とうとしているスレッド関数が、待ち処理に入る前に

すでに終了していた場合はどうなるだろうか。


例えば、メイン処理のWaitForSingleObjectの直前に 2秒のスリープ

入れてみた場合、結果は以下のようになる。


[結果]

> メイン開始

> スレッド起動

> スレッド終了

> wait error

> メイン終了


上記のように、WaitForSingleObject失敗する。


この現象の原因と対処はまた次回。

[MFC]マルチスレッド1

マルチスレッド


[環境]

VS2008

WIN32コンソールアプリ(MFC共通ヘッダ追加)


スレッドとはCPU利用の単位のこと。


1つのプロセス(今回の例ではコンソールアプリ)の中に1つ以上のスレッドを持つ。


このスレッドが同一プロセス内で複数もって処理を行うことが

マルチスレッドとなる。


マルチスレッドの用法としては、規模の大きい計算などをメインの処理から分岐させ、

バックグラウンドで非同期に処理を行わせるイメージ。

(実際はちょっと意味合いは違う。イメージ。)


組込などのRTOSなどでも使われ方が多少違ってくると思う。



以下、簡単なスレッドの生成と、その流れをコードで。

スレッドの種類にはワーカースレッドユーザーインターフェーススレッド

があるが、今回はワーカースレッドについて。


// スレッド関数

UINT ThreadFunc(LPVOID param)
{
  printf("スレッド起動\n");


  Sleep(1000);


  printf("スレッド終了\n");


  return 0;
}


// メイン処理

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
  int nRetCode = 0;


  printf("メイン開始\n");


  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
  {
    ・・・中略

  }
  else
  {
    CWinThread *pThread = AfxBeginThread(ThreadFunc, NULL);

  }
  printf("メイン終了\n");

  return nRetCode;
}

main からAfxBeginThread()関数を呼び、スレッドを生成している。

渡す引数は、実行するスレッド用の関数のポインタと、それに渡すパラメータ

を指定すればとりあえずOK。


AfxBeginThread はCWinThread 型のポインタを返すので、受け取る。

(今回は特に使用しない。)


今回はThreadFuncという作成したスレッド用関数と、その関数の引数となる

値だけを渡している。


(上記例は、スレッド用関数は引数が不要なのでNULLとしている。

 第2引数はvoid型のポインタである必要があるので、実際に引数を

 使用する際は、元の型にキャストするなどして使用する。)


スレッド関数で行っていることは、処理の突入/終了を表示することと、

その間に1秒間のスリープをしているだけである。


先に述べたように、メインの処理とは非同期で処理を行うので、上記コードを

実行すると次のような結果となる。

(処理速度によっては変わることもあると思う。)


[結果]

> メイン開始

> メイン終了

> スレッド起動


このように、非同期で処理が進むためというのと、メイン処理がシンプルすぎることも

あってか、メイン処理が終わってからやっとスレッドが起動している。

しかも、スレッド終了が呼ばれていない。


この時どうなるかというと、メインはすでに終了しているのにスレッドだけが動いている

ということで、メモリリークが発生する。


つまり、これに対応するには、メインが終了する前にスレッドの終了を

待つ必要がでてくる。


対応方法については次回

[MFC]簡易版split系関数

MFCアプリからカンマ区切りの数値データを読み込んで、配列に格納。


[環境]

VS2008

WIN32コンソールアプリ(MFC共通ヘッダ追加)


テキスト読み込みについてはこちら


サンプルデータ

[test.txt]

1,2,3

4,5,6


※ 行データに不正な値(「,」をはさんで何もないとか)でない、かつ列固定の場合


int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
  int nRetCode = 0;

  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
  {
    ・・・中略
  }
  else
  {
    CStdioFile file(_T("C:\\test.txt"), CStdioFile::modeRead);
    
    CString str;
    
    while(file.ReadString(str))
    {
      int split[3] = {0};

      TCHAR *next;
      TCHAR *token = wcstok_s(str.GetBuffer(), _T(","), &next);
      int i = 0;
      do
      {
        split[i] = _wtoi(token);
        token = wcstok_s(NULL, _T(","), &next);
        i++;
      } while(token != NULL);

    }
  }

  return nRetCode;
}


wcstok_s でデリミタ(区切り文字)までの文字を読み込んで、TCHAR型のtokenに格納。


格納した文字列を _wtoi で数値変換。


ループ内でのwcstok_s の第一引数がNULLが何故かは知らない。


MSDNを参考にしたので。


もっと可変長データを扱いたいとかあったら、VectorなりCArrayなり使うと

いけるんじゃないかな。


今必要なのはコレで十分だからここまで。


Perlだとこんなの一瞬なのに、Cは文字列系のサポート弱いのなんの。

[MFC]テキストの読み込み

MFCアプリからテキストを読み込んで、コンソール表示する。


[環境]

VS2008

WIN32コンソールアプリ(MFC共通ヘッダ追加)


読み込むテキストとして


[test.txt]

123

456


というテストデータを用意した。


int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
  int nRetCode = 0;

  // MFC を初期化して、エラーの場合は結果を印刷します。
  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
  {
    ・・・中略

  }
  else
  {
    // ファイルオープン
    CStdioFile file(_T("C:\\test.txt"), CStdioFile::modeRead);
    
    CString str;
    while(file.ReadString(str))
    {
      _tprintf(str.GetBuffer());
      _tprintf(_T("\n"));
    }
  }

  return nRetCode;
}


CStdioFileクラスのReadStringメソッドは読み込んだファイルを1行読み込む。


MFCはCの原始的な処理は意外と苦手っぽいきがする。

それにしてもマルチバイト文字列は大嫌いです。

[日記]送別

前の会社の先輩が東京に行くため、本日送別会。




間に合わなかった。。。




お世話になりました。




またいいアニメあったら教えてください。