#pragma twice

KAB-studio > プログラミング > #pragma twice > 284 Version 14.17 スレッドで変数を共有する

#pragma twice 284 Version 14.17 スレッドで変数を共有する

前のページへ 表紙・目次へ 次のページへ

 Version 14.17
スレッドで変数を共有する

これまでスレッドを作ってみたけど、これだけだとはっきり言って使い道
がありません
ってゆーかちゃんと使ってなかったね
スレッドを使う場合の基本方針は

サブスレッドで処理をする。
          ↓
サブスレッドで変数に値をセットする。
          ↓
メインスレッドでそれを使う

というものです
サブスレッドでなんかして、それをメインスレッドで使うわけね
そういうこと。たとえばサブスレッドでファイル検索をして、その結果を 
CStringArray に追加、メインスレッドでそれを呼び出して画面表示、みた
いな
ん? スレッドでやっても……あ、スレッドでやると画面が止まらない
ってこと?
そういうこと。画面表示用のダイアログはメインスレッドで動かすから、
サブスレッドのファイル検索をしていても止まらないんです
なるほど
パスを表示しなくても、プログレスダイアログを表示したりとかね
プログレスダイアログ?
進行状況を表示するダイアログ。ゲージが描かれてて、進行状況に合わせ
て1目盛りずつ増えていくっていうあれ
ああ、アレね。そか、あれもダイアログだから、あの表示と、ファイル検
索とかの処理とで別スレッドにしなきゃいけないんだ……
そういう場合、これまでの WaitForSingleObject() を使うような単純な処
理じゃうまくいきません
だよね、メインスレッド止まっちゃったらダイアログ動かなくなっちゃう
もん
そこで、こういった場合の処理について見てみます。まず重要な点は

・スレッド同士は変数を共有できる

ってこと
それって当たり前じゃないの?
まぁそうなんだけどね。プロセスの時はできなかったでしょ
あ、そういえば……プロセスはアドレスとかポインタとかが別だから共有
できないんだもんね
そう。スレッドの場合、ひとつのプロセスの中での話だから、お互いに同
じ変数を使うことができます
だから、サブスレッドがセットしたデータをメインスレッドが見る、とか
できるわけね
そういうこと。というわけで、実際にその例を見てみましょう

// 共有する変数。
int g_iData = 0;

// 止めるかどうかのフラグ。
bool g_isRun = true;

// 別スレッドで呼び出される、 g_iData を増やし続ける関数。
void __cdecl IncrementThread( void *p_p )
{
    TRACE( "スレッド開始。\n" );
    while( g_isRun )
    {
        ++g_iData;
        // 0.5 秒待ちます。
        Sleep( 500 );
    }
    TRACE( "スレッド終了。\n" );
}

// IncrementThread() を別スレッドとして呼び出す関数。
// この関数をボタンのイベントハンドラとかで呼んでください。
void CallThread()
{
    _beginthread( IncrementThread, 0, NULL );
}

// スレッドを止める関数。
// この関数をボタンのイベントハンドラとかで呼んでください。
void StopThread()
{
    g_isRun = false;
}

// g_iData を出力する関数。
// この関数をボタンのイベントハンドラとかで呼んでください。
void Output()
{
    TRACE( "%d\n", g_iData );
}

長! って、変数ふたつと小さな関数4つだけね
このうち

void CallThread()
void StopThread()
void Output()

を、ダイアログのボタンのイベントハンドラから呼ぶようにしてくださ

ボタン押して呼べるようにするわけね
準備ができたら、まずこれを呼びます

// IncrementThread() を別スレッドとして呼び出す関数。
// この関数をボタンのイベントハンドラとかで呼んでください。
void CallThread()
{
    _beginthread( IncrementThread, 0, NULL );
}

って、単にスレッド作って呼ぶだけね
その呼ばれるのがこれ

// 別スレッドで呼び出される、 g_iData を増やし続ける関数。
void __cdecl IncrementThread( void *p_p )
{
    TRACE( "スレッド開始。\n" );
    while( g_isRun )
    {
        ++g_iData;
        // 0.5 秒待ちます。
        Sleep( 500 );
    }
    TRACE( "スレッド終了。\n" );
}

 while でループしてる……
この while で見てる変数はこのグローバル変数

// 止めるかどうかのフラグ。
bool g_isRun = true;

これが true の間は回り続けます
ってことは、最初は回りっぱなしね
回っている間、グローバル変数 g_iData を増やし続けます。

// 共有する変数。
int g_iData = 0;

そのあとの Sleep() は?
これは、普通にループをさせるとすごい勢いで回って他のアプリに迷惑が
掛かるから
そういえば前にそんな話があったような
 Version 7.18 ( No.138 ) だね
そっか、タイマーの時だ
 CPU の性能全部使うんじゃなく、一定時間毎に休むようにすることで他
のアプリも処理できるようにするわけ
そうすれば他に迷惑掛けないわけねー
自分自身にもね
自分自身?
これはサブスレッドだから、これがすごい勢いで回るとメインスレッドに
影響が出ちゃうでしょ
なるほど、そのためにもってわけね
そういうこと。さて、このスレッドを呼ぶと、グローバル変数 g_iData 
が少しずつ増えていきます

// 共有する変数。
int g_iData = 0;

これを出力するのが Output() 

void Output()
{
    TRACE( "%d\n", g_iData );
}

スレッドを作ってから、この関数を呼ぶと
数字が出力されて……少し待ってから押すと増えてる!
こういうふうに、グローバル変数をふたつのスレッドで共有できます
ってゆーか普通に使えるんだね
それと、〈押せる〉ってことも大事
そか、ダイアログの方はメインスレッド、数字を増やしてる方は
サブスレッド、だからダイアログが止まらないんだね
で、最後にこのスレッドを止めてみます

// スレッドを止める関数。
// この関数をボタンのイベントハンドラとかで呼んでください。
void StopThread()
{
    g_isRun = false;
}

 g_isRun に false を入れると……

    while( g_isRun )

が false になるからループ抜けて、 IncrementThread() からも抜けて終
わり、ってわけね
そういうこと。こういうふうに、フラグをひとつ作って終了用に使うのは
よく使う方法のひとつだから
そういえば、これって止めないでアプリ終了させちゃったらどうなる
の?
メインスレッドが終了すると、サブスレッドは強制終了されます
強制!?
そう、今回の例なら〈スレッド終了〉ってメッセージが出ないで突然終了
しちゃうってこと
それはまずいね……
だから、ちゃんとする場合には、閉じるボタンとかでこのフラグを立てる
ようにします
おお
加えて、 WaitForSingleObject() でこのスレッドが終了するのを待ちま

え、待つの?
フラグ立てたからってすぐサブスレッドが終了するとは限らないからね。
ちゃんとサブスレッドが終了するまで待ってから、メインスレッドを終了さ
せるようにします
そこまでしなきゃいけないんだ……
特に、ちゃんとした後始末が必要なファイル出力やネットワーク処理とか
は特にね

/*
    Preview Next Story!
*/
スレッドは細かいとこ気を付けなきゃいけないから大変
その中でも一番大変なのがデッドロック
あの固まって動かなくなっちゃうやつね
というわけで次回はその解決方法
というわけで次回
< Version 14.18 クリティカルセクションを使おう! >
につづく!
って、残ってた最後の同期オブジェクトじゃない
でも全然違うんだこれが

 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。