この記事は、
Win32並行処理プログラミング入門39の続きです。前回はミューテックス・カーネルオブジェクトを解説しました。今回はセマフォ・カーネルオブジェクトを解説します。
今まで数個の同期オブジェクトを解説してきましたが、まだ解説するべき同期オブジェクトが残っています。それが今回解説を始める、セマフォ・カーネルオブジェクトです。
分かりやすいように、ミューテックス・カーネルオブジェクトと同じ内容のサンプルを使用して解説を行います。
#include <iostream>
#include <windows.h>
#include <tchar.h>
#include <process.h>
using namespace std;
//インスタンスのポインタとパラメータを保持するクラス
class InstanceAndParameter
{
private:
void* ins;
void* param;
public:
InstanceAndParameter( void* ins, void* param )
: ins( ins ), param( param ){}
void* getInstance() const { return ins; }
void* getParameter() const { return param; }
};
//並行処理を意識したクラス
class ConcurrentFoo
{
private:
HANDLE _semaphore;
long _number;
public:
//セマフォの初期化と破棄が必要
ConcurrentFoo() : _number( 0 )
{
//最大値を1にするとミューテックスに近くなる
this->_semaphore = CreateSemaphore(
__nullptr,
1, //利用可能なリソースの数
1, //リソースの最大値
__nullptr );
}
~ConcurrentFoo()
{
CloseHandle( this->_semaphore );
}
long getNumber() const { return _number; }
//並列的に実行するメソッド
//※並行度は高いが遅い
void AddFunc( int count )
{
LONG semaphoreCount;
for ( int i = 0; i < count; i++ ) {
WaitForSingleObject( this->_semaphore, INFINITE );
++this->_number;
ReleaseSemaphore( this->_semaphore, 1, &semaphoreCount );
}
}
//スレッドから呼び出すメソッド
static unsigned __stdcall Add( void* pvParam )
{
//パラメータのチェック
_ASSERTE( FALSE == IsBadReadPtr( pvParam,
sizeof( InstanceAndParameter ) )
&& "スレッドに渡されたパラメータが無効です" );
//インスタンスとパラメータを取り出してメソッドを実行
InstanceAndParameter* ip =
reinterpret_cast< InstanceAndParameter* >( pvParam );
ConcurrentFoo* obj =
reinterpret_cast< ConcurrentFoo* >( ip->getInstance() );
int count = PtrToInt(
reinterpret_cast< INT_PTR >( ip->getParameter() ) );
obj->AddFunc( count );
return 0;
}
};
int _tmain( int, _TCHAR* )
{
//スレッド数の判定
const int threadCount = 64;
if ( threadCount > MAXIMUM_WAIT_OBJECTS ) {
cout << "【エラー】" << endl;
cout << "指定するスレッド数が多すぎます。" << endl;
cout << "指定するスレッド数は"
<< MAXIMUM_WAIT_OBJECTS
<< "以下にして下さい。" << endl;
return -1;
}
//スレッドのパラメータを用意する
ConcurrentFoo obj;
int count = 100000;
InstanceAndParameter threadparam(
reinterpret_cast< void * >( &obj ),
reinterpret_cast< void * >( ( INT_PTR ) count ) );
//スレッドを作成する
HANDLE hThreads[ threadCount ];
for ( int i = 0; i < threadCount; i++ )
{
hThreads[ i ] = reinterpret_cast< HANDLE >(
_beginthreadex (
__nullptr,
0U,
ConcurrentFoo::Add,
reinterpret_cast< void * >( &threadparam ) ,
CREATE_SUSPENDED, //直ぐには実行しない
__nullptr ) );
}
//作成したスレッドを実行
for ( int i = 0; i < threadCount; i++)
ResumeThread( hThreads[ i ] );
//タイムアウト時間をミリ単位で指定して待機
//※指定した時間は正確ではありません
DWORD result = WaitForMultipleObjects(
threadCount, hThreads, TRUE, 20000 );
//WaitForSingleObject関数が終了した原因を表示
cout << "【スレッドの状態を表示します】" << endl;
unsigned int max = ( WAIT_OBJECT_0 + threadCount - 1 );
if ( result == WAIT_FAILED ) {
//エラーを表示する
DWORD error = GetLastError();
LPVOID title = _T( "エラー" );
LPVOID msg;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
__nullptr,
GetLastError(),
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // 既定の言語
reinterpret_cast< LPTSTR >( &msg ),
0,
__nullptr
);
MessageBox( __nullptr,
reinterpret_cast< LPCTSTR >( msg ) ,
reinterpret_cast< LPCTSTR >( title ),
MB_OK | MB_ICONERROR );
LocalFree( msg );
return -1;
}
if ( result == WAIT_TIMEOUT ) {
cout << "タイムアウトしてしまいました。" << endl;
} else if ( ( result >= WAIT_OBJECT_0 ) & ( result <= max ) ) {
cout << "全スレッドの処理が終わりました。" << endl;
}
cout << endl;
//変数の合計値を表示
long correctValue = threadCount * count;
long number = obj.getNumber();
cout << "【変数の値を表示します】" << endl;
cout << "予想値:" << correctValue << endl;
cout << "実際の値:" << number << endl;
cout << "不足値:" << ( correctValue - number ) << endl;
//ハンドルを閉じる
for ( int i = 0; i < threadCount; i++ )
CloseHandle( hThreads[ i ] );
cout << endl;
return 0;
}
このサンプルは、ミューテックス・カーネルオブジェクトの解説時に使用したものと同じ内容です。違う部分はConcurrentFoo クラスだけです。同期オブジェクトの隠蔽を行うと、この様に変更に強いオブジェクトが作れます。
長くなりましたので、セマフォ・カーネルオブジェクトの詳しい解説は次回から行います。
テーマ : プログラミング
ジャンル : コンピュータ