Visual Studio でマルチスレッド アプリケーションをデバッグする
スレッドは、オペレーティング システムでプロセッサ時間を許可する命令のシーケンスです。 オペレーティング システムで実行されているプロセスは、いずれも 1 つ以上のスレッドで構成されます。 複数のスレッドで構成されるプロセスをマルチスレッド プロセスといいます。
複数のプロセッサ、マルチコア プロセッサ、またはハイパースレッディング プロセッサを搭載したコンピューターでは、複数の同時スレッドを実行できます。 多くのスレッドを使用する並列処理では、プログラムのパフォーマンスを大幅に向上させることができますが、多くのスレッドを追跡するため、デバッグが困難になる場合もあります。
完全な並列処理は、常に実現できるとは限りません。 スレッドの同期が必要になることがあります。 スレッドは、別のスレッドが結果を生成するまで待機したり、別のスレッドが使用しているリソースに排他的にアクセスしたりすることが必要な場合があります。 同期に関する問題は、マルチスレッド アプリケーションで発生するバグの一般的な原因です。 また、スレッドは、決して利用可能にならないリソースを待機することがあります。 その結果、デッドロックと呼ばれる状態になります。
スレッドとプロセス
スレッドとプロセスは、コンピューター科学における関連概念です。 いずれも特定の順序で実行する必要がある命令のシーケンスを表します。 ただし、別個のスレッドやプロセスの命令は並列実行できます。
プロセスは、オペレーティング システム内に存在するものであり、ユーザーがプログラムやアプリケーションとして認識するものに相当します。 一方、スレッドはプロセス内に存在します。 このため、スレッドは、軽量プロセスと呼ばれることもあります。 各プロセスは、1 つ以上のスレッドで構成されます。
複数のプロセスが存在すると、コンピューターは、同時に複数のタスクを実行できます。 複数のスレッドが存在すると、プロセスで作業を分割して並列実行できます。 マルチプロセッサを搭載したコンピューターでは、プロセスやスレッドを各プロセッサに振り分けて実行できます。 これにより、真の並列処理が実現されます。
マルチスレッド アプリをデバッグするためのツール
Visual Studio には、マルチスレッド アプリのデバッグに使用できるさまざまなツールが用意されています。
スレッドの場合、デバッグのための主なツールは、 [スレッド] ウィンドウ、ソース ウィンドウのスレッド マーカー、 [並列スタック] ウィンドウ、 [並列ウォッチ] ウィンドウ、 [デバッグの場所] ツール バーです。 [スレッド] ウィンドウと [デバッグの場所] ツール バーの詳細については、「チュートリアル: [スレッド] ウィンドウを使用してデバッグする [並列スタック] ウィンドウと [並列ウォッチ] ウィンドウの使用方法については、「マルチ スレッド アプリケーションのデバッグの開始」を参照してください。 どちらのトピックでも、スレッド マーカーを使用する方法が示されています。
タスク並列ライブラリ (TPL) または同時実行ランタイムを使用するコードの場合、デバッグのための主要なツールは、 [並列スタック] ウィンドウ、 [並列ウォッチ] ウィンドウ、および JavaScript もサポートする [タスク] ウィンドウです。 作業を始めるときは、「チュートリアル: 並行アプリケーションのデバッグ」および「チュートリアル: C++ AMP アプリケーションのデバッグ」を参照してください。
GPU 上のスレッドのデバッグの場合の主要なツールは、 [GPU スレッド] ウィンドウです。 「方法:GPU スレッド ウィンドウを使用する」を参照してください。
プロセスの場合の主要なツールは、 [プロセスにアタッチ] ダイアログ ボックス、 [プロセス] ウィンドウ、 [デバッグの場所] ツール バーです。
Visual Studio には、強力なブレークポイントとトレースポイントも用意されており、マルチスレッド アプリケーションのデバッグに役立ちます。 個々のスレッドにブレークポイントを配置するには、ブレークポイントの条件とフィルターを使用します。 トレースポイントを使用すると、中断なしにプログラムの実行をトレースして、デッドロックなどの問題を調べることができます。 詳細については、「ブレークポイント アクションとトレースポイント」を参照してください。
ユーザー インターフェイスを含むマルチスレッド アプリケーションは、特にデバッグが困難になることがあります。 アプリケーションを別のコンピューターで実行して、リモート デバッグを使用することを検討してください。 詳細については、「リモート デバッグ」を参照してください。
各ツールで提供される情報と実行できる操作を次の表に示します。
ユーザー インターフェイス | 提供される情報 | 実行できる操作 |
---|---|---|
[プロセスにアタッチ] ダイアログ ボックス | アタッチが可能なプロセス - プロセス名 (.exe) - プロセス ID 番号 - メニュー バーのタイトル - 種類 ([マネージド v4.0]、[マネージド (v2.0、v1.1、v1.0)]、[x86]、[x64]、[IA64]) - ユーザー名 (アカウント名) - セッション番号 |
アタッチ先のプロセスの選択。 リモート コンピューターの選択。 リモート コンピューターに接続するためのトランスポートの種類の変更。 |
[プロセス] ウィンドウ | アタッチされたプロセス - プロセス名 - プロセス ID 番号 - .exe を処理するパス - メニュー バーのタイトル - 状態 (中断、実行中) - デバッグ (ネイティブ、マネージドなど) - トラスポートの種類 (既定値、認証なしのネイティブ) - トラスポートの修飾子 (リモート コンピューター) |
ツール: - アタッチ - デタッチ - 終了する ショートカット メニューでの操作 : - アタッチ - デタッチ - デバッグの停止時にデタッチ - 終了する |
[スレッド] ウィンドウ | 現在のプロセス内のスレッド - スレッド ID - マネージド ID - カテゴリ (メイン スレッド、インターフェイス スレッド、リモート プロシージャ コール ハンドラー、またはワーカー スレッド) - スレッド名 - スレッドが作成された場所 - 優先度 - 関係マスク - 中断回数 - プロセス名 - フラグ インジケーター - 中断中のインジケーター |
ツール: - 検索 - 呼び出し履歴の検索 - マイ コードのみにフラグを設定 - カスタム モジュール選択にフラグを設定 - グループ化 - 列 - 呼び出し履歴の展開または折りたたみ - グループの展開または折りたたみ - スレッドの凍結または解凍 ショートカット メニューでの操作 : - ソースのスレッドを表示 - スレッドへの切り替え - 実行中のスレッドの凍結 - 凍結されたスレッドの解凍 - さらに調査するスレッドにフラグを設定 - スレッドのフラグの解除 - スレッド名の変更 - スレッドの表示と非表示 その他の操作 : - スレッドの呼び出し履歴のデータヒントでの表示 |
ソース ウィンドウ | 左端余白のスレッド インジケーターは、スレッドが 1 つか複数かを示します (既定ではオフで、[スレッド] ウィンドウのショートカット メニューを使用してオンにすることができます)。 | ショートカット メニューでの操作 : - スレッドへの切り替え - さらに調査するスレッドにフラグを設定 - スレッドのフラグの解除 |
[デバッグの場所] ツール バー | - 現在のプロセス - アプリケーションを一時停止する - アプリケーションを再開する - アプリケーションを一時停止およびシャットダウンする - 現在のスレッド - 現在のスレッドのフラグ状態を切り替える - フラグが設定されたスレッドのみを表示する - 現在のプロセスのみ表示する - 現在のスタック フレーム |
- 別のプロセスへの切り替え - アプリケーションを一時停止、再開、またはシャットダウンする - 現在のプロセス内の別なスレッドへの切り替え - 現在のスレッド内の別なスタック フレームへの切り替え - 現在のスレッドのフラグ設定またはフラグ解除 - フラグが設定されたスレッドのみを表示する - 現在のプロセスのみ表示する |
[並列スタック] ウィンドウ | - 1 つのウィンドウにおける複数のスレッドの呼び出し履歴。 - 各スレッドのアクティブなスタック フレーム。 - メソッドの呼び出し元と呼び出し先。 - デッドロック検出 |
- 指定したスレッドをフィルターで除外 - 外部コード スタックをフィルターで除外 - [タスク] ビューへの切り替え - スレッドのフラグ設定またはフラグ解除 - ズーム - スタック フレームのコピー - すべてのスタックを画像として保存/エクスポート |
[並列ウォッチ] ウィンドウ | - フラグ列。特に注意する必要のあるスレッドをマークできます。 - フレーム列。矢印は選択したフレームを示します。 - 構成可能な列。マシン、プロセス、タイル、タスク、スレッドを表示できます。 |
- スレッドのフラグ設定またはフラグ解除 - フラグが設定されたスレッドのみ表示する - フレームを切り替える - 列を並べ替える - スレッドをグループ化する - スレッドを凍結または解凍する - [並列ウォッチ] ウィンドウ内のデータをエクスポートする |
[タスク] ウィンドウ | - タスク ID、タスクの状態 (スケジュール済み、実行中、待機中、デッドロック)、タスクに割り当てられているスレッドなど、Task オブジェクトに関する情報の表示。 - 呼び出し履歴での現在の位置。 - 作成時にタスクに渡されたデリゲート |
- 現在のタスクへの切り替え - タスクのフラグ設定またはフラグ解除 - タスクの凍結または解凍 |
[GPU スレッド] ウィンドウ | - フラグ列。特に注意する必要のあるスレッドをマークできます。 - 現在のスレッド列。黄色の矢印で現在のスレッドが示されます。 - [スレッド数] 列。同じ位置のスレッドの数を表示します。 - [行] 列。スレッドの各グループがあるコード行を表示します。 - [アドレス] 列。スレッドの各グループがある命令アドレスを表示します。 - [場所] 列。アドレスのコード内の位置です。 - [ステータス] 列。スレッドがアクティブであるかブロックされているかどうかを示します。 - [タイル] 列。行内のスレッドのタイル インデックスを示します。 |
- 別のスレッドに変更する - 特定のタイルとスレッドを表示する - 列の表示と非表示を切り替える - 列で並べ替える - スレッドをグループ化する - スレッドを凍結または解凍する - スレッドのフラグ設定またはフラグ解除 - フラグが設定されたスレッドのみ表示する |