#pragma twice

KAB-studio > プログラミング > #pragma twice > 281 Version 14.14 スレッドを作ってみる

#pragma twice 281 Version 14.14 スレッドを作ってみる

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

 Version 14.14
スレッドを作ってみる

今回はスレッドを作ってみます
スレッドって Version 14.01 ( No.268 ) で説明したのだよね、プログラ
ムの流れってゆー
そうです。スレッドはプログラムの流れ。プロセスひとつにつき、最初に
ひとつ作られて、プログラムを進めて行きます
そういう意味じゃ、プロセスの数だけスレッドがあるわけね
そういうこと。で、今回は、スレッドをひとつのプロセスの中でもうひと
つ作ってみます
おお! つまり最初の流れとは別にもうひとつ流れを作るわけね
そういうこと
……あれ? でもさ、 Version 14.04 ( No.271 ) で……

ミューテックス ( Mutex )
セマフォ ( Semaphore )
タイマー ( Waitable timer )
イベント ( Event )
クリティカルセクション ( Critical Section )

って、同期オブジェクトって5つ紹介してたじゃない。最後の
クリティカルセクションってまだ教えてもらってないよ?
実は、他の同期オブジェクトと違って、クリティカルセクションだけは複
数のプロセスで共有できないんです
あ、なるほど。ミューテックスとか、名前が同じひとつのミューテックス
を ProcessA や ProcessB で使えたけど、クリティカルセクションはそうい
うのできないんだ
だから、スレッドの説明をしてからクリティカルセクションの説明をしま

おー
さて、スレッドを使う前にまず断っておくことがあります
お、なんでしょー
スレッドを作る方法には、次の5種類があります

・API の CreateThread() を使う
・ランタイムの _beginthread() を使う
・MFC の AfxBeginThread() を使う
・MFC の CWinThread を使う
・MFC のユーザーインターフェイススレッドを使う

多!
で、この中で使うのは

・ランタイムの _beginthread() を使う

だけです
他は使わないの?
使いません。その理由は……

・_beginthread() は一番簡単にマルチスレッドを使える。
・_beginthread() はランタイムの同期を取ってくれる。
・MFC はマルチスレッドに向いていない。

といったところ。特に重要なのは〈ランタイムの同期を取る〉という点
これ以外は取らないの?
取らないんです。ランタイムはグローバル変数を多用してるから、普通に
複数のスレッドでランタイムを使うと問題が起きます。でも _beginthread() 
を使うと自動的に同期を取ってくれるから
複数のスレッドがいっぺんにアクセスしない!
というわけ。最後の MFC だけど、 MFC はスレッドをかなり怪しい使い方
してるから、マルチスレッドと組み合わせて使わない方がいいんです
それって MFC のプロジェクトで使っちゃいけないってこと?
そうじゃなくて、新しく作ったスレッドで、 MFC を使っちゃいけない、
ってこと
???
この辺は、実際にスレッドを使ってみないとわかんないかも……
じゃ、使お、使お!
はいはい。まず、スレッドのランタイム関数を使う時には process.h と
いうヘッダーファイルをインクルードする必要があります。 StdAfx.h にこ
んな感じに追加してみて

#include <process.h> // 追加。

//{{AFX_INSERT_LOCATION}}

〈//{{AFX_INSERT_LOCATION}}〉って書かれているところを探して、その
上にでも追加してください
インクルードファイルについては Version 6.04 ( No.104 ) さんしょー
では、実際に使ってみます

// 別スレッドで呼び出される関数。
void __cdecl ThreadFunction( void *p_p )
{
    TRACE( "スレッドから。\n" );
}

// スレッドを作って呼び出す関数。
void UseThread()
{
    _beginthread( ThreadFunction, 0, NULL );
}

ダイアログにボタンを貼り付けて UseThread() の方を呼ぶようにしてく
ださい
えっと

・ThreadFunction() : 呼ばれる方
・UseThread()      : 呼ぶ方

だよね
そういうこと。とりあえず使ってみて
ほい

スレッドから。

……いや、なんかすごく普通なんだけど……
まぁこれだけだとね。じゃあ関数の説明からしますまずは呼び出される方
から

// 別スレッドで呼び出される関数。
void __cdecl ThreadFunction( void *p_p )
{
    TRACE( "スレッドから。\n" );
}

 __cdecl って何度か出てきてるね
 Version 8.01 ( No.143 ) だね。関数には〈引数と戻り値の渡し方〉に
種類があって、主に

__cdecl
__stdcall

のどちらかが使われます
普通は __cdecl 、 API とか外から呼ばれるときは __stdcall なんだよ

そういうこと。スレッドで呼び出す関数は必ず中からだから __cdecl を
付けます
でもデフォルトが __cdecl なんだから付けなくてもいいんじゃない?
デフォルトは設定によるから、安心のために
ふーん
戻り値が void 、引数が void * なのは、 _beginthread() の引数がそう
なっているから
じゃあその呼び出す方

// スレッドを作って呼び出す関数。
void UseThread()
{
    _beginthread( ThreadFunction, 0, NULL );
}

第1引数の ThreadFunction は、関数のアドレスを渡してるってヤツね
そう、 Version 8.07 ( No.149 ) や Version 13.17 ( No.253 ) を参
照。 _beginthread() の第1引数には以下のような関数ポインタを渡す必要
があります

void( __cdecl *start_address )( void * )

んと

戻り値 (呼出方 *変数)(引数) 

なんだよね。だから戻り値が void 、引数 void * 、あと __cdecl 付け
る、ってわけね
そういうこと。この関数ポインタを渡すことで、 _beginthread() が中で
別スレッドとして ThreadFunction() を呼んでくれます
ウィンドウプロシージャとかと同じ仕組みね
そういうこと。じゃあ引数の説明の続き。第2引数は 0 でOK。第3引
数は、呼び出す関数の引数に渡す変数のポインタです

たとえば……

void UseThread()
{
    _beginthread( ThreadFunction, 0, "あいうえお" );
}

という形で文字列のポインタを渡すと

void __cdecl ThreadFunction( void *p_p )
{
    TRACE( "%s\n", (char *)p_p );
    // あいうえお
}

あ、 p_p にさっきの〈あいうえお〉が入ってる!
つまり、 _beginthread() の第3引数に渡したポインタが、呼び出し先関
数の void * ポインタに入ってるってこと
なるほど、そうしないと引数渡せないもんね。 void * ってことは、どん
なポインタでもOK?
OK。 Version 7.04 ( No.124 ) で説明したように、汎用ポインタとし
て使えるから。構造体のポインタとか渡せば一度に複数の変数も渡せるし
なるほど。……でさ、やっぱいまいちスレッドってよくわからないんだけ

というわけで次回に続く!

/*
    Preview Next Story!
*/
やっぱ使ってみないとよくわかんないよね
特にスレッドはノウハウの塊だから
それをしっかり教えてもらわなきゃね
憶えるのも大変だけどね
う”
というわけで次回
< Version 14.15 スレッドを使ってみる! >
につづく!
こういう時は思考もマルチスレッドにしてみるとか
憶えるとこはひとつなんだけど
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。