次の方法で共有


Xbox 360 および Windows でのマルチコアのコーディング

何年もの間、プロセッサの性能は着実に向上しており、ゲームやその他のプログラムは特別なことをすることなく、この電力の増加の利点を得てきました。

ルールが変更されました。 1 つのプロセッサ コアのパフォーマンスは、少しでも非常に遅くなっています。 ただし、一般的なコンピューターまたはコンソールで使用できるコンピューティング能力は、引き続き増加しています。 違いは、このパフォーマンスの向上の大部分は、1 台のコンピューター (多くの場合、1 つのチップ) に複数のプロセッサ コアがあることです。 Xbox 360 CPU には 1 つのチップに 3 つのプロセッサ コアがあり、2006 年に販売された PC プロセッサの約 70% がマルチコアでした。

使用可能な処理能力の増加は以前と同じくらい劇的ですが、開発者はこの機能を使用するためにマルチスレッド コードを記述する必要があります。 マルチスレッド プログラミングには、新しい設計とプログラミングの課題が伴います。 このトピックでは、マルチスレッド プログラミングを開始する方法についていくつかのアドバイスを提供します。

グッドデザインの重要性

マルチスレッド プログラムの優れた設計は重要ですが、非常に困難な場合があります。 主要なゲーム システムを別のスレッドに意図的に移動すると、各スレッドがほとんどの時間を他のスレッドで待機していることがわかります。 この種の設計により、複雑さが増し、デバッグ作業が大幅に増加し、実質的にパフォーマンスが向上しません。

スレッドがデータを同期または共有する必要があるたびに、データの破損、同期のオーバーヘッド、デッドロック、複雑さの可能性があります。 そのため、マルチスレッド設計では、すべての同期と通信ポイントを明確に文書化する必要があり、そのような点は可能な限り最小限に抑える必要があります。 スレッドが通信する必要がある場合、コーディング作業が増加し、ソース コードに影響を与えすぎると生産性が低下する可能性があります。

マルチスレッドの最も簡単な設計目標は、コードを大きな独立した部分に分割することです。 その後、これらの部分をフレームごとに数回だけ通信するように制限すると、過度の複雑さなしでマルチスレッドが大幅に高速化されます。

一般的なスレッド化されたタスク

いくつかの種類のタスクは、個別のスレッドに配置されるのに対して優れた実績があります。 次の一覧は完全なものではありませんが、いくつかのアイデアを提供する必要があります。

表示

レンダリングは、シーン グラフのウォークや、場合によっては D3D 関数の呼び出しのみを含む場合があり、多くの場合、CPU 時間の 50% 以上を占めます。 そのため、レンダリングを別のスレッドに移動すると、大きな利点があります。 更新スレッドは、レンダリングスレッドが処理できる何らかのレンダリング記述バッファーを埋めることができます。

ゲームの更新スレッドは、常にレンダー スレッドの 1 フレーム前です。つまり、ユーザーの操作が画面に表示されるまでに 2 つのフレームが必要になります。 この待機時間の増加は問題になる可能性がありますが、ワークロードの分割によるフレーム レートの増加は、通常、合計待機時間を許容可能な状態に保ちます。

ほとんどの場合、すべてのレンダリングは 1 つのスレッドで行われますが、ゲームの更新とは異なるスレッドです。

D3DCREATE_MULTITHREADED フラグは、1 つのスレッドでのレンダリングと、他のスレッドでのリソースの作成を許可するために使用される場合があります。このフラグは Xbox 360 では無視されるため、Windows では使用しないでください。 Windows では、このフラグを指定すると、D3D が強制的に同期にかなりの時間を費やし、レンダリング スレッドの速度が低下します。

ファイルの圧縮解除

読み込み時間は常に長すぎるため、フレーム レートに影響を与えずにデータをメモリにストリーミングするのは困難な場合があります。 すべてのデータがディスク上で積極的に圧縮されている場合、ハードドライブまたは光ディスクからのデータ転送速度が制限要因になる可能性は低くなります。 シングルスレッド プロセッサでは、通常、読み込み時間に役立つ圧縮に十分なプロセッサ時間がありません。 ただし、マルチプロセッサ システムでは、ファイルの圧縮解除では、それ以外の場合は無駄になる CPU サイクルが使用されます。読み込み時間とストリーミングが向上します。ディスク上のスペースを節約します。

運用環境で実行する必要がある処理の代わりに、ファイルの圧縮解除を使用しないでください。 たとえば、レベルの読み込み中に XML データの解析に余分なスレッドを割り当てる場合、マルチスレッドを使用してプレーヤーのエクスペリエンスを向上させる必要はありません。

ファイル圧縮解除スレッドを使用する場合でも、データ読み取り効率を最大化するために、非同期ファイル I/O と大きな読み取りを使用する必要があります。

グラフィックスの毛羽

ゲームの外観を改善する多くのグラフィカルな素敵さがありますが、厳密には必要ありません。 これには、手続き的に生成されたクラウド アニメーション、布と髪のシミュレーション、手続き型波、手続き型植生、より多くの粒子、または非ゲームプレイの物理学などが含まれます。

これらの効果はゲームプレイに影響しないため、複雑な同期の問題を引き起こすことはありません。フレームごとに 1 回、またはあまり頻繁に他のスレッドと同期できます。 さらに、Windows 用ゲームでは、これらの効果はマルチコア CPU を搭載したゲーマーの価値を高めることができますが、シングルコア コンピューターではサイレントモードで省略されるため、幅広い機能を簡単にスケーリングできます。

物理計算

多くの場合、ゲームの更新プログラムでは通常、物理計算の結果がすぐに必要になるため、物理を別のスレッドに配置して、ゲームの更新プログラムと並行して実行することはできません。 マルチスレッド物理学の代わりに、複数のプロセッサで実行することもできます。 これは可能ですが、共有データ構造に頻繁にアクセスする必要がある複雑なタスクです。 物理ワークロードをメインスレッドに収まるほど低く維持できれば、ジョブは簡単になります。

複数のスレッドでの物理演算の実行をサポートするライブラリを使用できます。 ただし、これは問題につながる可能性があります。ゲームが物理を実行している場合、多くのスレッドが使用されますが、残りの時間ではほとんど使用できません。 複数のスレッドで物理演算を実行するには、ワークロードがフレーム上で均等に分散されるように、これに対処する必要があります。 マルチスレッド物理エンジンを記述する場合は、すべてのデータ構造、同期ポイント、負荷分散に注意する必要があります。

マルチスレッド デザインの例

Windows 用ゲームは、CPU コアの数が異なるコンピューターで実行する必要があります。 ほとんどのゲーム マシンにはコアが 1 つだけありますが、2 コア マシンの数は急速に増加しています。 Windows の一般的なゲームでは、更新とレンダリングのためにワークロードを 1 つのスレッドに分割し、追加機能を追加するためのオプションのワーカー スレッドを使用する場合があります。 さらに、ファイル I/O とネットワークを実行するための一部のバックグラウンド スレッドが使用される可能性があります。 図 1 は、メインデータ転送ポイントと共にスレッドを示しています。

図 1. Windows 用ゲームでのスレッド設計

ウィンドウ用ゲームのスレッド設計

一般的な Xbox 360 ゲームでは、CPU を集中的に使用する追加のソフトウェア スレッドを使用できるため、図 2 に示すように、ワークロードが更新スレッド、レンダリング スレッド、3 つのワーカー スレッドに分割される可能性があります。

図 2. Xbox 360 用ゲームのスレッド設計

xbox 360 用ゲームのスレッド設計

ファイル I/O とネットワークを除き、これらのタスクはすべて、独自のハードウェア スレッド上にあるという利点を得るのに十分な CPU 負荷がかかる可能性があります。 これらのタスクには、通信せずにフレーム全体に対して実行できる十分な独立した可能性もあります。

ゲーム更新スレッドは、コントローラーの入力、AI、物理を管理し、他の 4 つのスレッドの手順を準備します。 これらの命令はゲーム更新スレッドが所有するバッファーに配置されるため、命令が生成されるため、同期は必要ありません。

フレームの最後に、ゲームの更新スレッドは命令バッファーを他の 4 つのスレッドに引き離し、次のフレームで作業を開始し、別の命令バッファーのセットを入力します。

更新スレッドとレンダリング スレッドは互いにロックステップで動作するため、通信バッファーは単に 2 つのバッファーに格納されます。更新スレッドは、レンダリング スレッドが他方から読み取っている間、いつでも 1 つのバッファーを埋めています。

他のワーカー スレッドは、必ずしもフレーム レートに関連付けられているとは限りません。 データの一部を圧縮解除すると、フレームよりもはるかに少ない場合や、多くのフレームが必要になる場合があります。 布や髪のシミュレーションでも、更新頻度が低い場合は許容される可能性があるため、フレーム レートで正確に実行する必要がない場合があります。 したがって、これら 3 つのスレッドは、更新スレッドとレンダー スレッドと通信するために異なるデータ構造を必要とします。 それぞれ、作業要求を保持できる入力キューが必要であり、レンダリング スレッドには、スレッドによって生成された結果を保持できるデータ キューが必要です。 各フレームの最後に、更新スレッドはワーカー スレッドのキューに作業要求のブロックを追加します。 フレームごとに 1 回だけリストにを追加すると、更新スレッドによって同期オーバーヘッドが最小限に抑えられます。 各ワーカー スレッドは、次のようなループを使用して、作業キューから割り当てを可能な限り迅速にプルします。

for(;;)
{
    while( WorkQueueNotEmpty() )
    {
        RemoveWorkItemFromWorkQueue();
        ProcessWorkItem();
        PutResultInDataQueue();
    }
    WaitForSingleObject( hWorkSemaphore ); 
}

データは更新スレッドからワーカー スレッドに、次にレンダリング スレッドに移動するため、一部のアクションが画面に表示されるまでに 3 つ以上のフレームの遅延が発生する可能性があります。 ただし、待機時間に耐えるタスクをワーカー スレッドに割り当てると、これは問題になりません。

別の設計では、同じ作業キューからすべての描画を行うワーカー スレッドが複数あることになります。 これにより、自動負荷分散が行われ、すべてのワーカー スレッドがビジー状態になる可能性が高くなります。

ゲームの更新スレッドは、ワーカー スレッドに多くの作業を与えすぎないように注意する必要があります。そうしないと、作業キューが継続的に増加する可能性があります。 更新スレッドでこれを管理する方法は、ワーカー スレッドが実行しているタスクの種類によって異なります。

同時マルチスレッドとスレッド数

すべてのスレッドが等しく作成されるわけではありません。 2 つのハードウェア スレッドは、別々のチップ上、同じチップ上、または同じコア上にある場合があります。 ゲーム プログラマが注意すべき最も重要な構成は、1 つのコア上の 2 つのハードウェア スレッド (同時マルチスレッド (SMT) または Hyper-Threading テクノロジ (HT テクノロジ) です。

SMT または HT テクノロジのスレッドは、CPU コアのリソースを共有します。 実行ユニットを共有するため、2 つの独立したハードウェア スレッドから可能な 100% ではなく、1 つではなく 2 つのスレッドを実行する場合の最大高速化は通常 10 ~ 20% です。

さらに重要なのは、SMT または HT テクノロジ のスレッドが L1 命令とデータ キャッシュを共有することです。 メモリ アクセス パターンに互換性がない場合は、キャッシュをめぐって戦い、多くのキャッシュ ミスを引き起こす可能性があります。 最悪の場合、2 番目のスレッドが実行されると、CPU コアの合計パフォーマンスが実際に低下する可能性があります。 Xbox 360 では、これは非常に簡単な問題です。 Xbox 360 の構成は既知であり、それぞれ 2 つのハードウェア スレッドを持つ 3 つの CPU コアであり、開発者はソフトウェア スレッドを特定の CPU スレッドに割り当て、スレッド設計によって追加のパフォーマンスが得られるかどうかを測定できます。

Windows では、状況はより複雑になります。 スレッドの数とその構成はコンピューターによって異なり、構成の決定は複雑です。 GetLogicalProcessorInformation 関数は、さまざまなハードウェア スレッド間の関係に関する情報を提供します。この関数は、Windows Vista、Windows 7、および Windows XP SP3 で使用できます。 したがって、現時点では、使用可能な "実際の" スレッドの数を決定するために、CPUID 命令と Intel と AMD によって提供されるアルゴリズムを使用する必要があります。 詳細については、リファレンスを参照してください。

DirectX SDK の CoreDetection サンプルには、 GetLogicalProcessorInformation 関数または CPUID 命令を使用して CPU コア トポロジを返すサンプル コードが含まれています。 現在のプラットフォームで GetLogicalProcessorInformation がサポートされていない場合は、CPUID 命令が使用されます。 CoreDetection は、次の場所にあります。

ソース:

DirectX SDK root\Samples\C++\Misc\CoreDetection

実行:

DirectX SDK root\Samples\C++\Misc\Bin\CoreDetection.exe

最も安全な前提は、CPU コアあたり 1 つの CPU 集中型スレッドを 1 つ以下にすることです。 CPU コアよりも CPU 負荷の高いスレッドを使用すると、利点がほとんどまたはまったくなくなり、追加のスレッドのオーバーヘッドと複雑さが増します。

スレッドの作成

スレッドの作成は非常に簡単な操作ですが、多くの潜在的なエラーがあります。 次のコードは、スレッドを作成し、終了を待ってからクリーンアップする適切な方法を示しています。

const int stackSize = 65536;
HANDLE hThread = (HANDLE)_beginthreadex( 0, stackSize,
            ThreadFunction, 0, 0, 0 );
// Do work on main thread here.
// Wait for child thread to complete
WaitForSingleObject( hThread, INFINITE );
CloseHandle( hThread );

...

unsigned __stdcall ThreadFunction( void* data )
{
#if _XBOX_VER >= 200
    // On Xbox 360 you must explicitly assign
    // software threads to hardware threads.
    XSetThreadProcessor( GetCurrentThread(), 2 );
#endif
    // Do child thread work here.
    return 0;
}

スレッドを作成するときは、子スレッドのスタック サイズを指定するか、0 を指定するかを選択できます。この場合、子スレッドは親スレッドのスタック サイズを継承します。 スレッドの開始時にスタックが完全にコミットされる Xbox 360 では、多くの子スレッドが親ほどスタックを必要としないため、0 を指定すると大量のメモリが無駄になる可能性があります。 Xbox 360 では、スタック サイズが 64 KB の倍数になることも重要です。

CreateThread 関数を使用してスレッドを作成すると、Windows で C/C++ ランタイム (CRT) が正しく初期化されません。 代わりに CRT _beginthreadex 関数を使用することをお勧めします。

CreateThread または _beginthreadex からの戻り値はスレッド ハンドルです。 このスレッドを使用すると、子スレッドが終了するのを待つことができます。これは、スレッドの状態を確認するループでスピンするよりもはるかに簡単で効率的です。 スレッドが終了するまで待機するには、スレッド ハンドルを使用して WaitForSingleObject を 呼び出します。

スレッドが終了し、スレッド ハンドルが閉じられるまで、スレッドのリソースは解放されません。 そのため、完了したら、スレッド ハンドルを CloseHandle で閉じる必要があります。 スレッドが WaitForSingleObject で終了するのを待機する場合は、待機が完了するまでハンドルを閉じないようにしてください。

Xbox 360 では、 XSetThreadProcessor を使用して、特定のハードウェア スレッドにソフトウェア スレッドを明示的に割り当てる必要があります。 それ以外の場合、すべての子スレッドは親と同じハードウェア スレッドに残ります。 Windows では、 SetThreadAffinityMask を使用して、スレッドを実行するハードウェア スレッドをオペレーティング システムに強く提案できます。 この手法は通常、システム上で実行されている他のプロセスがわからないので、Windows では避ける必要があります。 通常、Windows スケジューラでアイドル状態のハードウェア スレッドにスレッドを割り当てる方が良いです。

スレッドの作成は負荷の高い操作です。 スレッドはまれに作成および破棄する必要があります。 スレッドを頻繁に作成して破棄したい場合は、代わりに作業を待機するスレッドのプールを使用してください。

スレッドの同期

複数のスレッドを連携させるには、スレッドを同期し、メッセージを渡し、リソースへの排他アクセスを要求できる必要があります。 Windows と Xbox 360 には、豊富な同期プリミティブのセットが付属しています。 これらの同期プリミティブの詳細については、プラットフォームのドキュメントを参照してください。

排他アクセス

リソース、データ構造、またはコード パスへの排他的アクセスを取得することは、一般的なニーズです。 排他アクセスを取得するための 1 つのオプションはミューテックスであり、その一般的な使用法を次に示します。

// Initialize
HANDLE mutex = CreateMutex( 0, FALSE, 0 );

// Use
void ManipulateSharedData()
{
    WaitForSingleObject( mutex, INFINITE );
    // Manipulate stuff...
    ReleaseMutex( mutex );
}

// Destroy
CloseHandle( mutex );
The kernel guarantees that, for a particular mutex, only one thread at a time can 
acquire it.
The main disadvantage to mutexes is that they are relatively expensive to acquire 
and release. A faster alternative is a critical section.
// Initialize
CRITICAL_SECTION cs;
InitializeCriticalSection( &cs );

// Use
void ManipulateSharedData()
{
    EnterCriticalSection( &cs );
    // Manipulate stuff...
    LeaveCriticalSection( &cs );
}

// Destroy
DeleteCriticalSection( &cs );

クリティカル セクションはミューテックスと同様のセマンティクスを持ちますが、プロセス間ではなく、プロセス内でのみ同期するために使用できます。 メインの利点は、ミューテックスの約 20 倍高速に実行できる点です。

イベント

更新スレッドとレンダリング スレッドの 2 つのスレッドが、レンダリング記述バッファーのペアを使用して順番に実行されている場合は、特定のバッファーでいつ完了したかを示す方法が必要です。 これは、イベント ( CreateEvent に割り当てられた) を各バッファーに関連付けることによって行うことができます。 スレッドがバッファーで完了すると、 SetEvent を使用してこれを通知し、もう一方のバッファーのイベントで WaitForSingleObject を 呼び出すことができます。 この手法は、リソースのトリプル バッファリングに簡単に外挿します。

セマフォ

セマフォは、実行できるスレッドの数を制御するために使用され、一般的に作業キューを実装するために使用されます。 1 つのスレッドがキューに作業を追加し、新しい項目をキューに追加するたびに ReleaseSemaphore を使用します。 これにより、待機中のスレッドのプールから 1 つのワーカー スレッドを解放できます。 ワーカー スレッドは WaitForSingleObject を呼び出すだけで、返されると、キューに作業項目があることを認識します。 さらに、共有作業キューへの安全なアクセスを保証するために、クリティカル セクションまたはその他の同期手法を使用する必要があります。

SuspendThread を回避する

スレッドの動作を停止したい場合、正しい同期プリミティブではなく SuspendThread を使用したくなる場合があります。 これは常に悪い考えであり、デッドロックやその他の問題につながる可能性があります。 SuspendThread は、Visual Studio デバッガーと間違って対話します。 SuspendThread は使用しないでください。 代わりに WaitForSingleObject を 使用してください。

WaitForSingleObject と WaitForMultipleObjects

関数 WaitForSingleObject は、最もよく使用される同期関数です。 ただし、複数の条件が同時に満たされるまで、または一連の条件のいずれかが満たされるまでスレッドを待機したい場合があります。 この場合は、 WaitForMultipleObjects を使用する必要があります。

インターロック関数とロックレス プログラミング

ロックを使用せずに単純なスレッド セーフ操作を実行するための関数ファミリがあります。 これらは、 InterlockedIncrement などのインターロックされた関数ファミリです。 これらの関数と、フラグの慎重な設定を使用するその他の手法は、ロックレス プログラミングと呼ばれます。 ロックレス プログラミングは、正しく行うには非常に難しい場合があり、Xbox 360 では Windows よりもかなり困難です。

ロックなしのプログラミングの詳細については、「 Xbox 360 と Microsoft Windows のロックレス プログラミングに関する考慮事項」を参照してください。

同期の最小化

一部の同期方法は、他の同期方法よりも高速です。 ただし、可能な限り最速の同期手法を選択してコードを最適化するのではなく、通常は同期頻度を低くすることをお勧めします。 これは、頻繁に同期するよりも高速であり、デバッグが簡単なコードが簡単になります。

メモリ割り当てなどの一部の操作は、正しく動作するために同期プリミティブを使用する必要がある場合があります。 そのため、既定の共有ヒープから頻繁に割り当てを行うと、同期が頻繁になり、パフォーマンスが低下します。 頻繁な割り当てを回避したり、スレッドごとのヒープを使用したり (HeapCreate を使用する場合はHEAP_NO_SERIALIZEを使用)、この非表示の同期を回避できます。

非表示の同期のもう 1 つの原因はD3DCREATE_MULTITHREADEDです。これにより、Windows 上の D3D で多くの操作で同期が使用されます。 (このフラグは Xbox 360 では無視されます)。

スレッドごとのデータ (スレッド ローカル ストレージとも呼ばれます) は、同期を回避するための重要な方法です。 Visual C++ では、 __declspec(スレッド) 構文を使用して、グローバル変数をスレッドごとの変数として宣言できます。

__declspec( thread ) int tls_i = 1;

これにより、プロセス内の各スレッドにtls_iの独自のコピーが提供されます。これは、同期を必要とせずに安全かつ効率的に参照できます。

__declspec(スレッド) 手法は、動的に読み込まれた DLL では機能しません。 動的に読み込まれた DLL を使用する場合は、TLSAlloc 関数ファミリを使用してスレッド ローカル ストレージを実装する必要があります。

スレッドの破棄

スレッドを破棄する唯一の安全な方法は、メインスレッド関数からを返すか、スレッド呼び出し ExitThread または _endthreadexを使用して、スレッド自体を終了することです。 _beginthreadexを使用してスレッドが作成された場合は、ExitThread を使用しても CRT リソースが適切に解放されないため、_endthreadexを使用するか、メインスレッド関数から戻る必要があります。 スレッドが適切にクリーンアップされないため、 TerminateThread 関数を呼び出すことはありません。 スレッドは常に自殺する必要があります。彼らは決して殺されるべきではありません。

OpenMP

OpenMP は、プラグマを使用してループを並列化するコンパイラをガイドすることで、プログラムにマルチスレッドを追加するための言語拡張機能です。 OpenMP は Windows および Xbox 360 上の Visual C++ 2005 でサポートされており、手動スレッド管理と組み合わせて使用できます。 OpenMP はコードの一部をマルチスレッド化する便利な方法ですが、特にゲームに最適なソリューションになる可能性は低いです。 OpenMP は、処理アートやその他のリソースなどの実行時間の長い運用タスクにより適している場合があります。 詳細については、Visual C++ のドキュメントを参照するか、OpenMP Web サイトを参照してください。

プロファイル

マルチスレッド プロファイリングは重要です。 スレッドが互いに待機している長い屋台で終わるのは簡単です。 これらの屋台は、見つけて診断するのが難しい場合があります。 それらを識別するために、同期呼び出しにインストルメンテーションを追加することを検討してください。 サンプリング プロファイラーは、タイミング情報を大幅に変更することなく記録できるため、これらの問題を特定するのにも役立ちます。

タイミング

rdtsc 命令は、Windows で正確なタイミング情報を取得する方法の 1 つです。 残念ながら、rdtscには複数の問題があり、配送先住所の選択が不十分です。 rdtsc カウンターは必ずしも CPU 間で同期されるとは限らないので、スレッドがハードウェア スレッド間を移動すると、大きな正または負の違いが発生する可能性があります。 電源管理の設定によっては、rdtsc カウンターが増加する頻度も、ゲームの実行時に変化する可能性があります。 このような問題を回避するには、出荷ゲームで高精度なタイミングを実現するために 、QueryPerformanceCounterQueryPerformanceFrequency を使用することをお勧めします。 タイミングの詳細については、「 ゲームのタイミング」と「マルチコア プロセッサ」を参照してください。

デバッグ

Visual Studio では、Windows と Xbox 360 のマルチスレッド デバッグが完全にサポートされています。 Visual Studio の [スレッド] ウィンドウを使用すると、さまざまな呼び出し履歴とローカル変数を表示するためにスレッドを切り替えることができます。 スレッド ウィンドウでは、特定のスレッドをフリーズおよび解凍することもできます。

Xbox 360 では、watch ウィンドウで @hwthread メタ変数を使用して、現在選択されているソフトウェア スレッドが実行されているハードウェア スレッドを表示できます。

スレッドにわかりやすい名前を付ける場合は、スレッド ウィンドウを使用する方が簡単です。 Visual Studio やその他の Microsoft デバッガーを使用すると、スレッドに名前を付けられます。 次の SetThreadName 関数を実装し、起動時に各スレッドから呼び出します。

typedef struct tagTHREADNAME_INFO
{
    DWORD dwType;     // must be 0x1000
    LPCSTR szName;    // pointer to name (in user address space)
    DWORD dwThreadID; // thread ID (-1 = caller thread)
    DWORD dwFlags;    // reserved for future use, must be zero
} THREADNAME_INFO;

void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName )
{
    THREADNAME_INFO info;
    info.dwType = 0x1000;
    info.szName = szThreadName;
    info.dwThreadID = dwThreadID;
    info.dwFlags = 0;

    __try
    {
        RaiseException( 0x406D1388, 0,
                    sizeof(info) / sizeof(DWORD),
            (DWORD*)&info );
    }
    __except( EXCEPTION_CONTINUE_EXECUTION ) {
    }
}

// Example usage:
SetThreadName(-1, "Main thread");

カーネル デバッガー (KD) と WinDBG では、マルチスレッド デバッグもサポートされています。

テスト

マルチスレッドプログラミングは難しい場合があり、マルチスレッドバグの中にはめったに表示されないものもあります。見つけて修正するのが難しくなります。 それらをフラッシュする最適な方法の 1 つは、幅広いコンピューター、特に 4 つ以上のプロセッサを搭載したコンピューターでテストすることです。 シングルスレッド コンピューターで完全に動作するマルチスレッド コードは、4 プロセッサ コンピューターですぐに失敗する可能性があります。 AMD と Intel の CPU のパフォーマンスとタイミングの特性は大きく異なる可能性があるため、両方のベンダーの CPU に基づいてマルチプロセッサ コンピューターでテストしてください。

Windows Vista と Windows 7 の機能強化

新しいバージョンの Windows を対象とするゲームには、スケーラブルなマルチスレッド アプリケーションの作成を簡略化できる API が多数あります。 これは、新しい ThreadPool API といくつかの追加の同期プリミティブ (条件変数、スリムな読み取り/ライター ロック、および 1 回限りの初期化) で特に当てはまります。 これらのテクノロジの概要については、次の MSDN マガジンの記事を参照してください。

これらのオペレーティング システムで Direct3D 11 機能 を使用するアプリケーションでは、マルチスレッド レンダリングのスケーラビリティを向上するために、同時オブジェクト作成と遅延コンテキスト コマンド リストの新しい設計を利用することもできます。

まとめ

スレッド間の相互作用を最小限に抑える慎重な設計により、コードに過度の複雑さを加えることなく、マルチスレッド プログラミングのパフォーマンスを大幅に向上させることができます。 これにより、ゲーム コードはプロセッサの改善の次の波に乗り、これまで以上に魅力的なゲーム エクスペリエンスを提供できます。

リファレンス