#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のトップページをご覧ください。