Visual Studio には、マルチスレッド アプリケーションのデバッグに役立ついくつかのツールとユーザー インターフェイス要素が用意されています。 このチュートリアルでは、スレッド マーカー、 並列スタック ウィンドウ、 並列ウォッチ ウィンドウ、条件付きブレークポイント、フィルター ブレークポイントを使用する方法について説明します。 このチュートリアルを完了すると、マルチスレッド アプリケーションをデバッグするための Visual Studio の機能について理解できます。
次の 2 つの記事では、他のマルチスレッド デバッグ ツールの使用に関する追加情報を提供します。
[デバッグの場所] ツール バーと [スレッド] ウィンドウを使用するには、「チュートリアル: マルチスレッド アプリケーションをデバッグする」を参照してください。
Task (マネージド コード) とコンカレンシー ランタイム (C++) を使用するサンプルについては、「チュートリアル: 並列アプリケーションをデバッグする」を参照してください。 ほとんどのマルチスレッド アプリケーションの種類に適用される一般的なデバッグのヒントについては、その記事とこの記事の両方を参照してください。
最初の手順では、マルチスレッド アプリケーション プロジェクトを作成します。
マルチスレッド アプリ プロジェクトを作成する
Visual Studio を開き、新しいプロジェクトを作成します。
スタート ウィンドウが開いていない場合は、[ファイル]
[スタート ウィンドウ] 選択します。 スタート ウィンドウで、[新しいプロジェクト
作成] を選択します。 [ 新しいプロジェクトの作成 ] ウィンドウで、検索ボックスに コンソール と入力します。 次に、[言語] の一覧から [C#]、[ C++]、または [Visual Basic ] を選択し、[プラットフォーム] の一覧から [Windows ] を選択します。
言語フィルターとプラットフォーム フィルターを適用したら、.NET または C++ 用の コンソール アプリ テンプレートを選択し、[ 次へ] を選択します。
注
正しいテンプレートが表示されない場合は、 ツール>Get Tools and Features...に移動し、Visual Studio インストーラーを開きます。 .NET デスクトップ開発または C++ ワークロードを使用したデスクトップ開発を選択し、[変更] を選択します。
[新しいプロジェクトの構成] ウィンドウで、[プロジェクト名] ボックスに「MyThreadWalkthroughApp」と入力または入力します。 次に、[ 次へ ] または [ 作成] を選択します。どのオプションでも使用できます。
.NET Core または .NET 5 以降のプロジェクトの場合は、推奨されるターゲット フレームワークまたは .NET 8 を選択し、[ 作成] を選択します。
新しいコンソール プロジェクトが表示されます。 プロジェクトが作成されると、ソース ファイルが表示されます。 選択した言語によっては、ソース ファイルが Program.cs、 MyThreadWalkthroughApp.cpp、または Module1.vbと呼ばれる場合があります。
ソース ファイルに表示されるコードを削除し、次の更新されたコードに置き換えます。 コード構成に適したスニペットを選択します。
using System; using System.Threading; public class ServerClass { static int count = 0; // The method that will be called when the thread is started. public void InstanceMethod() { Console.WriteLine( "ServerClass.InstanceMethod is running on another thread."); int data = count++; // Pause for a moment to provide a delay to make // threads more apparent. Thread.Sleep(3000); Console.WriteLine( "The instance method called by the worker thread has ended. " + data); } } public class Simple { public static void Main() { for (int i = 0; i < 10; i++) { CreateThreads(); } } public static void CreateThreads() { ServerClass serverObject = new ServerClass(); Thread InstanceCaller = new Thread(new ThreadStart(serverObject.InstanceMethod)); // Start the thread. InstanceCaller.Start(); Console.WriteLine("The Main() thread calls this after " + "starting the new InstanceCaller thread."); } }
[ファイル] メニューの [すべてを保存] をクリックします。
(Visual Basic のみ)ソリューション エクスプローラー (右側のウィンドウ) で、プロジェクト ノードを右クリックし、[ プロパティ] を選択します。 [ アプリケーション ] タブで、 Startup オブジェクト を Simple に変更します。
マルチスレッド アプリをデバッグする
ソース コード エディターで、次のコード スニペットを探します。
Thread.Sleep
の左側の余白を左クリックするか、C++ の場合はstd::this_thread::sleep_for
ステートメントをクリックして新しいブレークポイントを挿入します。余白の赤い円は、ブレークポイントがこの場所に設定されていることを示します。
[ デバッグ ] メニューの [ デバッグの開始] (F5) を選択します。
Visual Studio によってソリューションがビルドされ、デバッガーがアタッチされた状態でアプリの実行が開始され、アプリがブレークポイントで停止します。
ソース コード エディターで、ブレークポイントを含む行を見つけます。
スレッド マーカーを検出する
[デバッグ] ツール バーで、[ソースにスレッドを表示] ボタンを選択します。
スレッドをソース します。
F11 キーを 2 回押してデバッガーを進める。
ウィンドウの左側にある余白を見てください。 この行では、2 つのねじれたスレッドに似た
アイコン に注目してください。 スレッド マーカーは、スレッドがこの場所で停止していることを示します。
スレッド マーカーは、ブレークポイントによって部分的に隠すことができます。
ポインターをスレッド マーカーの上に置きます。 停止した各スレッドの名前とスレッド ID 番号を示すデータヒントが表示されます。 この場合、名前はおそらく
<noname>
。スレッド マーカーを選択すると、ショートカット メニューで使用可能なオプションが表示されます。
スレッドの場所を表示する
[ 並列スタック ] ウィンドウでは、スレッド ビューと (タスク ベースのプログラミングの場合) タスク ビューを切り替え、各スレッドの呼び出し履歴情報を表示できます。 このアプリでは、スレッド ビューを使用できます。
[デバッグ>>] を選択して、[並列スタック] ウィンドウを開きます。 次のような内容が表示されます。 正確な情報は、各スレッドの現在の場所、ハードウェア、およびプログラミング言語によって異なる場合があります。
この例では、左から右に、マネージド コードに関するこの情報が表示されます。
- 現在のスレッド (黄色の矢印) は
ServerClass.InstanceMethod
に入った。 スレッドの上にマウス ポインターを置くと、スレッド ID とスタック フレームを表示ServerClass.InstanceMethod
。 - スレッド 31724 は、スレッド 20272 が所有するロックを待機しています。
- メインスレッド(左側)が[外部コード]で停止しており、[外部コードの 表示]を選択すると詳細に表示できます。
この例では、左から右に、マネージド コードに関するこの情報が表示されます。
- メイン スレッド (左側) は、スレッド マーカー アイコン
Thread.Start
によって停止ポイントが識別されるで停止しました。
- 2 つのスレッドが
ServerClass.InstanceMethod
に入り、そのうちの 1 つは現在のスレッド (黄色の矢印) ですが、もう一方のスレッドはThread.Sleep
で停止しています。 - 新しいスレッド (右側) も開始されていますが、
ThreadHelper.ThreadStart
で停止しています。
- 現在のスレッド (黄色の矢印) は
リスト ビューでスレッドを表示するには、 デバッグ>Windows>Threads を選択します。
このビューでは、スレッド 20272 がメイン スレッドであり、現在外部コード、特に System.Console.dll に位置していることを簡単に確認できます。
注
[スレッド] ウィンドウの使用方法の詳細については、「チュートリアル: マルチスレッド アプリケーションをデバッグする」を参照してください。
[ 並列スタック ] ウィンドウまたは [ スレッド ] ウィンドウでエントリを右クリックすると、ショートカット メニューで使用可能なオプションが表示されます。
これらの右クリック メニューからさまざまなアクションを実行できます。 このチュートリアルでは、 並列ウォッチ ウィンドウ (次のセクション) でこれらの詳細について詳しく説明します。
変数にウォッチを設定する
デバッグ>>> を選択して、並列ウォッチ ウィンドウを開きます。
<Add Watch>
テキストが表示されるセル (または 4 番目の列の空のヘッダー セル) を選択し、「data
」と入力します。各スレッドのデータ変数の値がウィンドウに表示されます。
<Add Watch>
テキストが表示されるセル (または 5 番目の列の空のヘッダー セル) を選択し、「count
」と入力します。各スレッドの
count
変数の値がウィンドウに表示されます。 この多くの情報がまだ表示されない場合は、 F11 キーを数回押して、デバッガーでスレッドの実行を進めてみてください。ウィンドウ内のいずれかの行を右クリックすると、使用可能なオプションが表示されます。
スレッドにフラグを設定してフラグを解除する
重要なスレッドを追跡し、他のスレッドを無視するようにスレッドにフラグを設定できます。
[並列ウォッチ] ウィンドウで、Shift キーを押しながら複数の行を選択します。
右クリックし、[ フラグ] を選択します。
選択したすべてのスレッドにフラグが設定されます。 これで、フラグが設定されたスレッドのみを表示するようにフィルター処理できます。
[並列ウォッチ] ウィンドウで、[フラグ付きスレッドのみを表示] ボタンを選択します。
を表示します。
フラグが設定されたスレッドのみが一覧に表示されます。
ヒント
一部のスレッドにフラグを設定したら、コード エディターでコード行を右クリックし、[ フラグ付きスレッドをカーソルに実行] を選択できます。 フラグが設定されたすべてのスレッドが到達するコードを必ず選択してください。 Visual Studio では、選択したコード行でスレッドが一時停止されるため、 スレッドの凍結と解凍によって実行順序を簡単に制御できます。
[ フラグ付きスレッドのみを表示 ] ボタンをもう一度選択して、[ すべてのスレッドの表示 ] モードに戻ります。
スレッドのフラグを解除するには、[ 並列ウォッチ ] ウィンドウで 1 つ以上のフラグ付きスレッドを右クリックし、[ フラグの解除] を選択します。
スレッドの実行を凍結および凍結解除する
ヒント
スレッドを凍結および凍結解除 (中断および再開) して、スレッドが作業を実行する順序を制御できます。 これは、デッドロックや競合状態などのコンカレンシーの問題を解決するのに役立ちます。
[ 並列ウォッチ ] ウィンドウで、すべての行が選択されている状態で右クリックし、[ 固定] を選択します。
2 番目の列には、各行に一時停止アイコンが表示されます。 一時停止アイコンは、スレッドが固定されていることを示します。
1 つの行のみを選択して、他のすべての行の選択を解除します。
行を右クリックし、[ 解凍] を選択します。
この行の一時停止アイコンは消え、スレッドが固定されなくなったことが示されます。
コード エディターに切り替えて 、F11 キーを押します。 凍結されていないスレッドだけが実行されます。
アプリでは、いくつかの新しいスレッドがインスタンス化される場合もあります。 新しいスレッドはフラグが立てられておらず、凍結されません。
条件付きブレークポイントを使用して 1 つのスレッドをフォローする
デバッガーで 1 つのスレッドの実行に従うと便利です。 これを行う 1 つの方法は、関心のないスレッドを凍結することです。 シナリオによっては、たとえば特定のバグを再現するために、他のスレッドをフリーズさせることなく 1 つのスレッドに従う必要がある場合があります。 他のスレッドをフリーズさせることなくスレッドをフォローするには、関心のあるスレッドを除いてコードに分割しないようにする必要があります。 このタスクは、 条件付きブレークポイントを設定することで実行できます。
スレッド名やスレッド ID など、さまざまな条件にブレークポイントを設定できます。 各スレッドに固有であることがわかっているデータに条件を設定すると便利です。 この方法は、特定のスレッドよりも特定のデータ値に関心がある場合に、デバッグ中に一般的です。
前に作成したブレークポイントを右クリックし、[条件] を選択 します。
[ ブレークポイントの設定] ウィンドウで、条件式の
data == 5
を入力します。ヒント
特定のスレッドに関心がある場合は、条件にスレッド名またはスレッド ID を使用します。 [ブレークポイントの設定] ウィンドウでこれを行うには、[条件式] ではなく [フィルター] を選択し、フィルターのヒントに従います。 デバッガーを再起動すると スレッド ID が変更されるため、アプリ コードでスレッドに名前を付ける必要がある場合があります。
[ ブレークポイントの設定] ウィンドウを閉じます。
[
RestartApp ボタン]を選択して、デバッグセッションを再開します。
データ変数の値が 5 であるスレッド上のコードに分割します。 [並列ウォッチ] ウィンドウで、現在のデバッガー コンテキストを示す黄色の矢印を探します。
これで、コード (F10) をステップ オーバーしてコード (F11) にステップ インし、単一スレッドの実行に従うことができます。
ブレークポイントの条件がスレッドに固有であり、デバッガーが他のスレッドの他のブレークポイントにヒットしない限り (無効にする必要がある場合があります)、他のスレッドに切り替えずにコードをステップオーバーしてコードにステップインできます。
注
デバッガーを進めると、すべてのスレッドが実行されます。 ただし、他のスレッドがブレークポイントに達しない限り、デバッガーは他のスレッドのコードに入ることができません。