1.実験テーマ
Windows スレッドのミューテックスと同期
2. 実験の目的
(1) オペレーティング システムのプロセスとスレッドの関連概念を確認し、Windows スレッドについての理解を深めます。
(2) ミューテックス オブジェクトを理解し、相互排他操作と同期操作を使用してプロデューサーとコンシューマーの問題に対する並行プログラムを作成し、P (つまり semWait)、V (つまり semSignal) プリミティブの理解を深め、P、V プリミティブを使用して処理を理解します。相互同期および相互排他操作
3. 実験内容(実験原理・使用した理論知識、アルゴリズム・プログラムフローチャート、手順と方法、キーコード)
生産者消費者問題
ステップ 1: 「Win32 コンソール アプリケーション」プロジェクトを作成し、リスト 5-1 のプログラムをコピーして、実行可能ファイルにコンパイルします。
書類。
ステップ 2: ステップ 1 で生成された実行可能ファイルを「コマンド プロンプト」ウィンドウで実行し、実行結果を一覧表示します。
ステップ 3: ソース プログラムを注意深く読み、スレッドを作成する WINDOWS API 関数を見つけて、次の質問に答えます。 最初のスレッド
最初の実行関数は何ですか (どこから始まりますか)? スレッドを作成する API 関数内のどのパラメータですか?
ステップ 4: コード リスト 5-1 のプログラムを変更し、コンシューマの数が より大きくなるようにプロデューサ スレッドとコンシューマ スレッドの数を調整します。
生産者の皆さん、結果の違いを見てください。実行の結果を見て、そこからどのような結論を導き出すことができますか?
ステップ 5: リスト 5-1 のプログラムを変更し、プログラムのコメントの指示に従ってセマフォ EmptySemaphore の初期化メソッドを変更します。
結果の違いを確認する方法。
ステップ 6: ステップ 4 の結果に基づいて、MSDN を参照して、次の質問に答えてください。
1) CreateMutex にはいくつかのパラメータがありますが、それらは何を意味しますか。
2) CreateSemaphore にはいくつかのパラメータがあります。それぞれは何を表しており、セマフォの初期値はどのパラメータにありますか。
3) プログラム内の P および V プリミティブに対応する実際の Windows API 関数は何か、これらのステートメントを書き出します。
4) CreateMutex を CreateSemaphore に置き換えることはできますか? セマフォ Mutex を完全に使用するようにプログラム 5-1 を変更してみてください。
CreateSemaphore および関連関数が実装されています。変更するステートメントを記述します。
質疑応答:
ステップ 3:
スレッドを作成する Windows API 関数は CreateThread です。
スレッドの最初の実行関数は、プロデューサー関数 (プロデューサー スレッドのエントリ関数) です。これは、CreateThread の 3 番目のパラメーター lpStartAddress にあります。
ステップ 4:
コンシューマの数がプロデューサの数よりも多くなるように、プロデューサ スレッドとコンシューマ スレッドの数を調整すると、次の結果が観察されます。
コンシューマ スレッドの数がプロデューサ スレッドの数よりも多い場合、コンシューマ スレッドはバッファ内の製品が消費されるまでより頻繁に待機する可能性があるため、コンシューマ スレッドの実行が遅くなる可能性があります。
コンシューマ スレッドがプロダクトを消費する速度が遅いため、プロデューサー スレッドはより頻繁にプロダクトを生成する可能性があり、バッファ内の空きスペースが少なくなり、プロデューサー スレッドが EmptySemaphore を待機する回数が増加する可能性があります。
ステップ5:
セマフォ EmptySemaphore の初期化メソッドを変更し、2 番目のパラメーターを 0 に設定します。つまり、空席数 0 に初期化します。コード例: EmptySemaphore = CreateSemaphore(NULL, 0, SIZE_OF_BUFFER, NULL)。
その結果、EmptySemaphore セマフォが最初は 0 であるため、すべてのプロデューサー スレッドは最初にプロダクトを生成できず、プロデューサー スレッドは WaitForSingleObject(EmptySemaphore, INFINITE) でブロックされ、コンシューマー スレッドがプロダクトを消費するのを待機します。
ステップ6:
CreateMutexには3つのパラメータがあり、それぞれの代表的な意味は以下の通りです。
パラメータ 1: lpMutexAttributes、SECURITY_ATTRIBUTES 構造体へのポインタ。ミューテックス オブジェクトのセキュリティ属性を設定するために使用されます。NULL にすることもできます。これは、デフォルトのセキュリティ属性を使用することを意味します。
パラメータ 2: bInitialOwner。呼び出しスレッドがミューテックス オブジェクトの初期所有権を持っているかどうかを示します。TRUE は所有されていることを意味し、FALSE は所有されていないことを意味します。
パラメータ 3: lpName、ミューテックス オブジェクトの名前。NULL も可能です。
CreateSemaphoreには4つのパラメータがあり、それぞれの代表的な意味は以下の通りです。
パラメータ 1: lpSemaphoreAttributes、SECURITY_ATTRIBUTES 構造体へのポインタ。セマフォのセキュリティ属性を設定するために使用されます。NULL にすることもできます。これは、デフォルトのセキュリティ属性を使用することを意味します。
パラメータ 2: lInitialCount、セマフォの初期カウンタ値、つまり利用可能なリソースの数。
パラメータ 3: lMinimumCount、セマフォの最大カウンタ値、つまりカウンタの上限。
パラメータ 4: lpName、セマフォの名前。NULL も可能です。セマフォの初期値は 2 番目のパラメーター lInitialCount にあります。
P および V プリミティブに対応する実際の Windows API 関数は次のとおりです。
P 操作は、オブジェクトの信号状態を待つために使用される WaitForSingleObject 関数に対応します。
V 操作は、セマフォのカウンター値を増やすために使用される ReleaseSemaphore 関数に対応します。
CreateMutex は CreateSemaphore で置き換えることができますが、いくつかの変更が必要です。
CreateMutex を CreateSemaphore に変更し、ミューテックスの初期化と使用メソッドを変更します。
セマフォの状態を待機するには、WaitForSingleObject(mutex, INFINITE) の代わりに WaitForSingleObject を使用します。
セマフォを解放するには、ReleaseMutex(mutex) の代わりに ReleaseSemaphore を使用します。
変更されたコード例:
// ミューテックスを初期化します
mutex = CreateSemaphore(NULL, 1, 1, NULL);
// プロデューサースレッド
WaitForSingleObject(emptySemaphore, INFINITE);
WaitForSingleObject(ミューテックス、INFINITE);
// ...
ReleaseSemaphore(mutex, 1, NULL);
// コンシューマスレッド
WaitForSingleObject(fullSemaphore, INFINITE);
WaitForSingleObject(ミューテックス、INFINITE);
// ...
ReleaseSemaphore(mutex, 1, NULL);
なお、CreateSemaphoreで初期化されるミューテックスカウンタの初期値は1であるため、ここではCreateSemaphoreのカウンタ初期値として1を使用し、WaitForSingleObjectとReleaseSemaphoreの第2パラメータに1を設定します。
4.実験結果と解析
実験結果:
プログラムは複数のプロデューサー スレッドとコンシューマー スレッドを作成します。
プロデューサ スレッドは製品の生成を担当し、各生成後に製品をバッファに入れます。
コンシューマ スレッドは、バッファから製品を取得して消費する責任があります。
同期と相互排他は、相互排他ロック (Mutex) とセマフォ (EmptySemaphore と FullSemaphore) を通じてプロデューサーとコンシューマーの間で実現されます。
実験分析:
main 関数では、最初に 1 つのミューテックス (Mutex) と 2 つのセマフォ (EmptySemaphore と FullSemaphore) が作成されます。ミューテックスは共有リソースへのアクセスを保護するために使用され、セマフォはプロデューサーとコンシューマーの同期を制御するために使用されます。
設定されたプロデューサーとコンシューマーの数に応じて、対応する数のスレッドが作成され、それぞれプロデューサーとコンシューマーの関数が実行されます。
各プロダクション スレッドの前に、プロデューサー スレッドはまず EmptySemaphore セマフォを待機して、バッファーに使用可能な位置があることを確認します。次に、ミューテックス Mutex を取得し、生成操作を実行し、その積をバッファに入れて、ミューテックス Mutex と FullSemaphore セマフォを解放します。
コンシューマー スレッドは毎回プロダクトを消費する前に、まず FullSemaphore セマフォを待って、バッファー内に利用可能なプロダクトがあることを確認します。次に、ミューテックス Mutex を取得し、消費操作を実行し、バッファから積を取り出し、ミューテックス Mutex と EmptySemaphore セマフォを解放します。
プロデューサおよびコンシューマの各操作では、生成された製品番号、バッファのステータス、消費された製品番号などの関連情報が出力ステートメントを介して出力されます。
main 関数の while ループは、プログラムの実行を制御するために使用されます。Enter キーが押されると、p_c continue が false に設定され、プロデューサーとコンシューマーのスレッドの実行が終了します。
マルチスレッドと同期が関係しているため、スレッドのスケジュールや競合状態の不確実性により、実際の結果は異なる場合があります。上記の分析は考えられる状況です。
このコードにはセマフォの作成時に 2 種類のコメントがあり、プログラムの動作への影響を観察するために必要に応じて変更できることに注意してください。
全体として、この実験は、プロデューサーとコンシューマーの動作をシミュレートすることにより、スレッド間の同期と相互排除のメカニズムを実証します。プロデューサとコンシューマは、バッファを介して対話し、バッファがいっぱいのときにプロデューサが生成を続けたり、バッファが空になったときにコンシューマが消費を続けたりしないようにします。ミューテックスとセマフォを合理的に使用することで、スレッドの安全性と正しい実行シーケンスが保証されます。
5. まとめと経験
1. マルチスレッド プログラミング: この実験には、複数のスレッドを作成し、スレッド同期メカニズムを使用してスレッド間の調整と通信を実現するマルチスレッド プログラミングが含まれます。
2. 生産者-消費者モデル: この実験は生産者-消費者モデルに基づいており、生産者は製品を生成してバッファーに置き、消費者は消費のためにバッファーから製品を取り出します。
3. バッファの使用: バッファは、配列で表される循環キューとして設計されています。プロデューサとコンシューマは、バッファの読み取りおよび書き込みポインタ (インおよびアウト) を維持することによってバッファにアクセスします。
4. スレッドの同期と相互排他: プロデューサーとコンシューマーが同時にバッファにアクセスすることによって引き起こされるデータの不整合の問題を回避するために、相互排他操作には相互排他セマフォ (Mutex) が使用されます。同時に、プロデューサーとコンシューマー間の同期は、同期セマフォ (FullSemaphore および EmptySemaphore) を通じて実現されます。
5. スレッド間通信: プロデューサとコンシューマはセマフォを介して通信および同期し、バッファがいっぱいの場合はプロデューサが待機し、バッファが空の場合はコンシューマが待機します。
6. 上記の実験を通じて、マルチスレッド プログラミングの概念と技術、およびマルチスレッド環境での生産者消費者モデルの適用について深く理解できます。これは、同時プログラミングとシステム設計に重要な意味を持ちます。