このチュートリアルでは、[ 並列タスク] ウィンドウと [ 並列スタック] ウィンドウを使用して並列アプリケーションをデバッグする方法について説明します。 これらのウィンドウは、 タスク並列ライブラリ (TPL) または コンカレンシー ランタイムを使用するコードの実行時の動作を理解して確認するのに役立ちます。 このチュートリアルでは、ブレークポイントが組み込まれているサンプル コードを提供します。 コードが中断された後、チュートリアルでは、[ 並列タスク] ウィンドウと [ 並列スタック] ウィンドウを使用して確認する方法を示します。
このチュートリアルでは、次のタスクについて説明します。
すべてのスレッドの呼び出し履歴を 1 つのビューで表示する方法。
アプリケーションで作成
System.Threading.Tasks.Task
インスタンスの一覧を表示する方法。スレッドではなくタスクの実際の呼び出し履歴を表示する方法。
[並列タスク] ウィンドウと [並列スタック] ウィンドウからコードに移動する方法。
ウィンドウがグループ化、ズーム、およびその他の関連機能を使用してスケールに対処する方法。
[前提条件]
このチュートリアルでは、[ マイ コードのみ ] が有効になっていることを前提としています (Visual Studio のより新しいバージョンでは既定で有効になっています)。 [ ツール ] メニューの [ オプション] を選択し、[ デバッグ ] ノードを展開し、[ 全般] を選択し、[ マイ コードのみを有効にする (管理のみ)] を選択します。 この機能を設定しない場合でも、このチュートリアルを使用できますが、結果が図とは異なる場合があります。
C# のサンプル
C# サンプルを使用する場合、このチュートリアルでは外部コードが非表示になっていることを前提としています。 外部コードを表示するかどうかを切り替えるには、[呼び出し履歴] ウィンドウの [名前] テーブル ヘッダーを右クリックし、[外部コードの表示] を選択またはオフにします。 この機能を設定しない場合でも、このチュートリアルを使用できますが、結果が図とは異なる場合があります。
C++ サンプル
C++ サンプルを使用する場合は、この記事の外部コードへの参照を無視できます。 外部コードは、C# サンプルにのみ適用されます。
図
この記事の図は、C# サンプルを実行しているクアッド コア コンピューターに記録されています。 他の構成を使用してこのチュートリアルを完了できますが、図はコンピューターに表示される内容とは異なる場合があります。
サンプル プロジェクトを作成する
このチュートリアルのサンプル コードは、何も実行しないアプリケーションを対象としています。 この演習の目的は、ツール ウィンドウを使用して並列アプリケーションをデバッグする方法を理解することです。
Visual Studio を開き、新しいプロジェクトを作成します。
スタート ウィンドウが開いていない場合は、[ファイル]
[スタート ウィンドウ] 選択します。 スタート ウィンドウで、[ 新しいプロジェクト] を選択します。
スタート ウィンドウで、[新しいプロジェクト
作成] を選択します。 [ 新しいプロジェクトの作成 ] ウィンドウで、検索ボックスに コンソール と入力します。 次に、[言語] の一覧から [C#]、[ C++]、または [Visual Basic ] を選択し、[プラットフォーム] の一覧から [Windows ] を選択します。
言語フィルターとプラットフォーム フィルターを適用したら、.NET Core または C++ 用 のコンソール アプリ を選択し、[ 次へ] を選択します。
注
正しいテンプレートが表示されない場合は、 ツール>Get Tools and Features...に移動し、Visual Studio インストーラーを開きます。 .NET デスクトップ開発または C++ ワークロードを使用したデスクトップ開発を選択し、[変更] を選択します。
[ 新しいプロジェクトの構成 ] ウィンドウで、名前を入力するか、[ プロジェクト名 ] ボックスに既定の名前を使用します。 次に、[ 次へ ] または [作成] を選択します。どのオプションでも使用できます。
.NET Core の場合は、推奨されるターゲット フレームワークまたは .NET 8 を選択し、[ 作成] を選択します。
新しいコンソール プロジェクトが表示されます。 プロジェクトが作成されると、ソース ファイルが表示されます。
プロジェクト内の.cpp、.cs、または.vbコード ファイルを開きます。 その内容を削除して、空のコード ファイルを作成します。
選択した言語の次のコードを空のコード ファイルに貼り付けます。
using System; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; class S { static void Main() { pcount = Environment.ProcessorCount; Console.WriteLine("Proc count = " + pcount); ThreadPool.SetMinThreads(4, -1); ThreadPool.SetMaxThreads(4, -1); t1 = new Task(A, 1); t2 = new Task(A, 2); t3 = new Task(A, 3); t4 = new Task(A, 4); Console.WriteLine("Starting t1 " + t1.Id.ToString()); t1.Start(); Console.WriteLine("Starting t2 " + t2.Id.ToString()); t2.Start(); Console.WriteLine("Starting t3 " + t3.Id.ToString()); t3.Start(); Console.WriteLine("Starting t4 " + t4.Id.ToString()); t4.Start(); Console.ReadLine(); } static void A(object o) { B(o); } static void B(object o) { C(o); } static void C(object o) { int temp = (int)o; Interlocked.Increment(ref aa); while (aa < 4) { ; } if (temp == 1) { // BP1 - all tasks in C Debugger.Break(); waitFor1 = false; } else { while (waitFor1) { ; } } switch (temp) { case 1: D(o); break; case 2: F(o); break; case 3: case 4: I(o); break; default: Debug.Assert(false, "fool"); break; } } static void D(object o) { E(o); } static void E(object o) { // break here at the same time as H and K while (bb < 2) { ; } //BP2 - 1 in E, 2 in H, 3 in J, 4 in K Debugger.Break(); Interlocked.Increment(ref bb); //after L(o); } static void F(object o) { G(o); } static void G(object o) { H(o); } static void H(object o) { // break here at the same time as E and K Interlocked.Increment(ref bb); Monitor.Enter(mylock); while (bb < 3) { ; } Monitor.Exit(mylock); //after L(o); } static void I(object o) { J(o); } static void J(object o) { int temp2 = (int)o; switch (temp2) { case 3: t4.Wait(); break; case 4: K(o); break; default: Debug.Assert(false, "fool2"); break; } } static void K(object o) { // break here at the same time as E and H Interlocked.Increment(ref bb); Monitor.Enter(mylock); while (bb < 3) { ; } Monitor.Exit(mylock); //after L(o); } static void L(object oo) { int temp3 = (int)oo; switch (temp3) { case 1: M(oo); break; case 2: N(oo); break; case 4: O(oo); break; default: Debug.Assert(false, "fool3"); break; } } static void M(object o) { // breaks here at the same time as N and Q Interlocked.Increment(ref cc); while (cc < 3) { ; } //BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q Debugger.Break(); Interlocked.Increment(ref cc); while (true) Thread.Sleep(500); // for ever } static void N(object o) { // breaks here at the same time as M and Q Interlocked.Increment(ref cc); while (cc < 4) { ; } R(o); } static void O(object o) { Task t5 = Task.Factory.StartNew(P, TaskCreationOptions.AttachedToParent); t5.Wait(); R(o); } static void P() { Console.WriteLine("t5 runs " + Task.CurrentId.ToString()); Q(); } static void Q() { // breaks here at the same time as N and M Interlocked.Increment(ref cc); while (cc < 4) { ; } // task 5 dies here freeing task 4 (its parent) Console.WriteLine("t5 dies " + Task.CurrentId.ToString()); waitFor5 = false; } static void R(object o) { if ((int)o == 2) { //wait for task5 to die while (waitFor5) { ;} int i; //spin up all procs for (i = 0; i < pcount - 4; i++) { Task t = Task.Factory.StartNew(() => { while (true);}); Console.WriteLine("Started task " + t.Id.ToString()); } Task.Factory.StartNew(T, i + 1 + 5, TaskCreationOptions.AttachedToParent); //scheduled Task.Factory.StartNew(T, i + 2 + 5, TaskCreationOptions.AttachedToParent); //scheduled Task.Factory.StartNew(T, i + 3 + 5, TaskCreationOptions.AttachedToParent); //scheduled Task.Factory.StartNew(T, i + 4 + 5, TaskCreationOptions.AttachedToParent); //scheduled Task.Factory.StartNew(T, (i + 5 + 5).ToString(), TaskCreationOptions.AttachedToParent); //scheduled //BP4 - 1 in M, 2 in R, 3 in J, 4 in R, 5 died Debugger.Break(); } else { Debug.Assert((int)o == 4); t3.Wait(); } } static void T(object o) { Console.WriteLine("Scheduled run " + Task.CurrentId.ToString()); } static Task t1, t2, t3, t4; static int aa = 0; static int bb = 0; static int cc = 0; static bool waitFor1 = true; static bool waitFor5 = true; static int pcount; static S mylock = new S(); }
コード ファイルを更新したら、変更を保存してソリューションをビルドします。
[ファイル] メニューの [すべてを保存] をクリックします。
[ ビルド ] メニューの [ ソリューションのリビルド] を選択します。
Debugger.Break
の呼び出しは 4 つあります (C++ サンプルのDebugBreak
)。 したがって、ブレークポイントを挿入する必要はありません。 アプリケーションを実行するだけで、デバッガーで最大 4 回中断します。
[並列スタック] ウィンドウの使用: スレッド ビュー
開始するには、[ デバッグ ] メニューの [ デバッグの開始] を選択します。 最初のブレークポイントがヒットするまで待ちます。
1 つのスレッドの呼び出し履歴を表示する
[ デバッグ ] メニューの [Windows ] をポイントし、[スレッド] を選択 します。 Visual Studio の下部にある [スレッド ] ウィンドウをドッキングします。
[ デバッグ ] メニューの [Windows ] をポイントし、[ 呼び出し履歴] を選択します。 Visual Studio の下部に [呼び出し履歴 ] ウィンドウをドッキングします。
スレッド ウィンドウでスレッドをダブルクリックして、最新の状態にします。 現在のスレッドには黄色の矢印があります。 現在のスレッドを変更すると、その呼び出し履歴が [ 呼び出し履歴 ] ウィンドウに表示されます。
[並列スタック] ウィンドウを調べる
[ デバッグ ] メニューの [Windows ] をポイントし、[ 並列スタック] を選択します。 左上隅のボックスで [ スレッド ] が選択されていることを確認します。
[並列スタック] ウィンドウを使用すると、1 つのビューで複数の呼び出し履歴を同時に表示できます。 次の図は、[呼び出し履歴] ウィンドウの上にある [並列スタック] ウィンドウを示しています。
メイン スレッドの呼び出し履歴が 1 つのボックスに表示され、他の 4 つのスレッドの呼び出し履歴が別のボックスにグループ化されます。 スタック フレームが同じメソッド コンテキストを共有するため、4 つのスレッドがグループ化されます。つまり、これらは同じメソッド ( A
、 B
、 C
) にあります。 同じボックスを共有するスレッドのスレッド ID と名前を表示するには、ヘッダー ([#] スレッド) を含むボックスの上にマウス ポインターを置きます。 現在のスレッドは太字で表示されます。
黄色の矢印は、現在のスレッドのアクティブなスタック フレームを示します。
スタック フレーム (モジュール名、 パラメーター型、 パラメーター名、 パラメーター値、 行番号 、 バイト オフセット) の詳細を設定するには、[ 呼び出し履歴 ] ウィンドウを右クリックします。
ボックスの周りの青い強調表示は、現在のスレッドがそのボックスの一部であることを示します。 現在のスレッドは、ツールヒントの太字のスタック フレームでも示されます。 [スレッド] ウィンドウで [メイン スレッド] をダブルクリックすると、[ 並列スタック ] ウィンドウの強調表示矢印がそれに応じて移動することを確認できます。
2 番目のブレークポイントまで実行を再開する
2 番目のブレークポイントがヒットするまで実行を再開するには、[ デバッグ ] メニューの [ 続行] を選択します。 次の図は、2 番目のブレークポイントにあるスレッド ツリーを示しています。
最初のブレークポイントでは、4 つのスレッドがすべて S.A から S.B から S.C メソッドに変換されます。 その情報は [ 並列スタック] ウィンドウに引き続き表示されますが、4 つのスレッドはさらに進んでいます。 そのうちの 1 つは S.D に続き、次に S.E.もう 1 つは S.F、S.G、S.H に続く。他の 2 人は S.I と S.J に続き、そこからそのうちの 1 つは S.K に、もう 1 つはユーザー以外の外部コードに進みました。
スタック フレームの上にマウス ポインターを合わせると、スレッド ID と他のフレームの詳細を確認できます。 青い強調表示は現在のスレッドを示し、黄色の矢印は現在のスレッドのアクティブなスタック フレームを示します。
ボックス ヘッダー ( 1 スレッド や 2 スレッドなど) にカーソルを合わせると、 スレッドのスレッド ID を確認できます。 スタック フレームの上にマウス ポインターを合わせると、スレッド ID と他のフレームの詳細を確認できます。 青い強調表示は現在のスレッドを示し、黄色の矢印は現在のスレッドのアクティブなスタック フレームを示します。
糸のアイコン(編み込まれた線)は、非アクティブなスレッドのアクティブなスタックフレームを示します。 [呼び出し履歴] ウィンドウで、S.B をダブルクリックしてフレームを切り替えます。 [並列スタック] ウィンドウは、曲線矢印アイコンを使用して、現在のスレッドの現在のスタック フレームを示します。
注
[並列スタック] ウィンドウ内のすべてのアイコンの説明については、「 並列スタックの使用」ウィンドウを参照してください。
[ スレッド ] ウィンドウでスレッドを切り替え、[ 並列スタック ] ウィンドウのビューが更新されていることを確認します。
[並列スタック] ウィンドウのショートカット メニューを使用して、別のスレッドまたは別のスレッドの別のフレームに切り替えることができます。 たとえば、S.J を右クリックし、[フレームに 切り替え] をポイントして、コマンドを選択します。
S.C を右クリックし、[ フレームに切り替え] をポイントします。 コマンドの 1 つに、現在のスレッドのスタック フレームを示すチェック マークがあります。 同じスレッドのそのフレームに切り替えることができます (曲線矢印のみが移動します)、または他のスレッドに切り替えることができます (青の強調表示も移動します)。 次の図はサブメニューを示しています。
メソッド コンテキストが 1 つのスタック フレームのみに関連付けられている場合、ボックス ヘッダーには 1 スレッド が表示され、ダブルクリックして切り替えることができます。 関連付けられているフレームが 1 つを超えるメソッド コンテキストをダブルクリックすると、メニューが自動的にポップアップ表示されます。 メソッド コンテキストにカーソルを合わせると、右側に黒い三角形が表示されます。 その三角形をクリックすると、ショートカット メニューも表示されます。
多数のスレッドを持つ大規模なアプリケーションの場合は、スレッドのサブセットだけに焦点を当てる必要があります。 [並列スタック] ウィンドウには、フラグが設定されたスレッドの呼び出し履歴のみを表示できます。 スレッドにフラグを設定するには、ショートカット メニューまたはスレッドの最初のセルを使用します。
ツール バーで、リスト ボックスの横にある [フラグ付きのみを表示 ] ボタンを選択します。
フラグが設定されたスレッドのみが [並列スタック] ウィンドウに表示されるようになりました。
3 番目のブレークポイントまで実行を再開する
3 番目のブレークポイントにヒットするまで実行を再開するには、[ デバッグ ] メニューの [ 続行] を選択します。
複数のスレッドが同じメソッド内にあるが、メソッドが呼び出し履歴の先頭になかった場合、メソッドは異なるボックスに表示されます。 現在のブレークポイントの例として、3 つのスレッドがあり、3 つのボックスに表示される S.L があります。 S.L をダブルクリックします。
S.L が他の 2 つのボックスに太字で表示されるので、他の場所を確認できます。 S.L に呼び出すフレームと呼び出すフレームを確認する場合は、ツール バーの [ メソッド ビューの切り替え ] ボタンを選択します。 次の図は、[ 並列スタック] ウィンドウのメソッド ビューを示しています。
選択したメソッドでダイアグラムがどのようにピボットされ、ビューの中央にある独自のボックスに配置されているかに注目してください。 呼び出し先と呼び出し元は、それぞれ上部と下部に表示されます。 [ メソッド ビューの切り替え ] ボタンをもう一度選択して、このモードを終了します。
[並列スタック] ウィンドウのショートカット メニューには、他にも次の項目があります。
16 進数表示 では、ツールヒントの数値を 10 進数と 16 進数の間で切り替えます。
シンボル設定 では、それぞれのダイアログ ボックスが開きます。
ソースにスレッドを表示 すると、ソース コード内のスレッド マーカーの表示が切り替わり、ソース コード内のスレッドの場所が表示されます。
外部コードを表示 すると、ユーザー コードに含まれていない場合でも、すべてのフレームが表示されます。 図を展開して、他のフレームに合わせて表示してみてください (シンボルがないため、淡色表示される可能性があります)。
[ 並列スタック ] ウィンドウで、ツール バーの [ 現在のスタック フレームまで自動スクロール ] ボタンがオンになっていることを確認します。
大きなダイアグラムがあるとき、次のブレークポイントに移動した際に現在のスレッドのアクティブなスタックフレームまでビューを自動スクロールさせたい場合があります。つまり、最初にブレークポイントに到達したスレッドです。
続行する前に、[ 並列スタック] ウィンドウで、左までスクロールし、下までスクロールします。
4 番目のブレークポイントまで実行を再開する
4 番目のブレークポイントにヒットするまで実行を再開するには、[ デバッグ ] メニューの [ 続行] を選択します。
ビューがどのように自動スクロールされているかに注目してください。 スレッドウィンドウでスレッドを切り替えるか、Call Stackウィンドウでスタック フレームを切り替えて、ビューが常に正しいフレームに自動的にスクロールされることを確認します。 [ 現在のツール フレームまで自動スクロール ] オプションをオフにして、違いを表示します。
鳥の目のビューは、[並列スタック] ウィンドウの大きな図にも役立ちます。 既定では、 鳥瞰図 はオンになっています。 ただし、次の図に示すように、ウィンドウの右下隅にあるスクロール バー間のボタンをクリックして切り替えることができます。
鳥瞰図では、四角形を移動して図の周りをすばやくパンできます。
ダイアグラムを任意の方向に移動するもう 1 つの方法は、ダイアグラムの空白領域を選択し、目的の場所にドラッグすることです。
図を拡大または縮小するには、Ctrl キーを押しながらマウス ホイールを動かします。 または、ツール バーの [ズーム] ボタンを選択し、[ズーム] ツールを使用します。
また、[ ツール ] メニューをクリックして [オプション] をクリックし、[ デバッグ ] ノードの下にあるオプションを選択またはオフにして、スタックを下方向ではなく上方向に表示することもできます。
続行する前に、[ デバッグ ] メニューの [ デバッグの停止 ] を選択して実行を終了します。
[並列タスク] ウィンドウと [並列スタック] ウィンドウの [タスク] ビューを使用する
続行する前に、前の手順を完了することをお勧めします。
最初のブレークポイントがヒットするまでアプリケーションを再起動します。
[ デバッグ ] メニューの [ デバッグの開始 ] を選択し、最初のブレークポイントがヒットするまで待ちます。
[ デバッグ ] メニューの [Windows ] をポイントし、[スレッド] を選択 します。 Visual Studio の下部にある [スレッド ] ウィンドウをドッキングします。
[ デバッグ ] メニューの [Windows ] をポイントし、[ 呼び出し履歴] を選択します。 Visual Studio の下部に [呼び出し履歴 ] ウィンドウをドッキングします。
スレッド ウィンドウでスレッドをダブルクリックして、最新の状態にします。 現在のスレッドには黄色の矢印があります。 現在のスレッドを変更すると、他のウィンドウが更新されます。 次に、タスクを確認します。
[ デバッグ ] メニューの [Windows] をポイントし、[ タスク] を選択します。 次の図は 、[タスク] ウィンドウを示しています。
実行中のタスクごとに、同じ名前のプロパティによってその ID、タスクを実行するスレッドの ID と名前、その位置情報を読み取ることができます。その位置情報にカーソルを合わせると、呼び出し履歴全体を表示するツールチップが現れます。 また、[ タスク ] 列の下には、タスクに渡されたメソッドが表示されます。つまり、開始点です。
任意の列を並べ替えることができます。 並べ替えの列と方向を示す並べ替えグリフに注目してください。 列を左または右にドラッグして並べ替えることもできます。
黄色の矢印は、現在のタスクを示します。 タスクを切り替えるには、タスクをダブルクリックするか、ショートカット メニューを使用します。 タスクを切り替えると、基になるスレッドが最新になり、他のウィンドウが更新されます。
あるタスクから別のタスクに手動で切り替えると、矢印のアウトラインは、非アクティブなタスクの現在のデバッガー コンテキストを示します。
タスク間で手動で切り替えると、黄色の矢印が移動しますが、白い矢印にはデバッガーが中断する原因となったタスクが表示されます。
2 番目のブレークポイントまで実行を再開する
2 番目のブレークポイントがヒットするまで実行を再開するには、[ デバッグ ] メニューの [ 続行] を選択します。
以前は、[ 状態] 列にすべてのタスクが [アクティブ] と表示されましたが、2 つのタスクが [ブロック] になりました。 タスクは、さまざまな理由でブロックされる 可能性があります。 [状態] 列で、待機中のタスクの上にマウス ポインターを置いて、ブロックされている理由を確認します。 たとえば、次の図では、タスク 11 がタスク 12 を待機しています。
以前は、[ 状態] 列にすべてのタスクが [アクティブ] と表示されましたが、2 つのタスクが [ブロック] になりました。 タスクは、さまざまな理由でブロックされる 可能性があります。 [状態] 列で、待機中のタスクの上にマウス ポインターを置いて、ブロックされている理由を確認します。 たとえば、次の図では、タスク 4 がタスク 5 を待機しています。
タスク 4 は、タスク 2 に割り当てられたスレッドが所有するモニターで待機しています。 ヘッダー行を右クリックし、列を選択して、タスク 2 の>値を表示します。
[タスク] ウィンドウの最初の列にあるフラグをクリックすると、タスクにフラグを 設定 できます。
フラグを使用すると、同じデバッグ セッション内の異なるブレークポイント間のタスクを追跡したり、[ 並列スタック ] ウィンドウに呼び出し履歴が表示されているタスクをフィルター処理したりできます。
前に [ 並列スタック] ウィンドウを 使用したときに、アプリケーション スレッドを表示しました。 [並列スタック] ウィンドウをもう一度表示しますが、今回はアプリケーション タスクを表示します。 これを行うには、左上のボックスで [タスク ] を選択します。 次の図は、タスク ビューを示しています。
現在タスクを実行していないスレッドは、[ 並列スタック ] ウィンドウの [タスク] ビューには表示されません。 また、タスクを実行するスレッドの場合、タスクに関連しないスタック フレームの一部は、スタックの上部と下部からフィルター処理されます。
[タスク] ウィンドウをもう一度表示します。 列ヘッダーを右クリックすると、列のショートカット メニューが表示されます。
ショートカット メニューを使用して、列を追加または削除できます。 たとえば、AppDomain 列は選択されていません。そのため、一覧には表示されません。 [親] を選択します。 親列は、4 つのタスクの値なしで表示されます。
3 番目のブレークポイントまで実行を再開する
3 番目のブレークポイントにヒットするまで実行を再開するには、[ デバッグ ] メニューの [ 続行] を選択します。
この実行例では、タスク 11 とタスク 12 が同じスレッドで実行されていることを確認します (非表示の場合は、[ スレッドの割り当て] 列を表示します)。 この情報は[ スレッド ]ウィンドウには表示されません。ここで見ることは 、[タスク] ウィンドウのもう 1 つの利点です。 これを確認するには、[ 並列スタック ] ウィンドウを表示します。 タスクを表示していることを確認します。 タスク 11 と 12 を見つけるには、[ 並列スタック ] ウィンドウでツールヒントをスキャンします。
タスク 5 という新しいタスクが実行され、タスク 4 が待機しています。 [状態] ウィンドウで待機中のタスクにカーソルを合わせると、その理由を確認できます。 [ 親 ] 列で、タスク 4 がタスク 5 の親であることに注意してください。
親子関係をより適切に視覚化するには、列ヘッダー行を右クリックし、[ 親子ビュー] を選択します。 次の図が表示されます。
タスク 4 とタスク 5 が同じスレッドで実行されていることに注意してください (非表示の場合は、[ スレッドの割り当て] 列が表示されます)。 この情報は[ スレッド ]ウィンドウには表示されません。ここで見ることは 、[タスク] ウィンドウのもう 1 つの利点です。 これを確認するには、[ 並列スタック ] ウィンドウを表示します。 タスクを表示していることを確認します。 タスク 4 と 5 を見つけるには、[ タスク] ウィンドウでタスクをダブルクリックします。 これを行うと、[ 並列スタック ] ウィンドウの青い強調表示が更新されます。 [ 並列スタック] ウィンドウでツールヒントをスキャンして、タスク 4 と 5 を見つけることもできます。
[ 並列スタック ] ウィンドウで、S.P を右クリックし、[ スレッドに移動] を選択します。 ウィンドウがスレッド ビューに切り替わると、対応するフレームが表示されます。 両方のタスクを同じスレッドで確認できます。
これは、[スレッド] ウィンドウと比較して、[並列スタック] ウィンドウの [タスク ビュー] のもう 1 つの利点です。
4 番目のブレークポイントまで実行を再開する
3 番目のブレークポイントにヒットするまで実行を再開するには、[ デバッグ ] メニューの [ 続行] を選択します。 ID で並べ替える ID 列ヘッダーを選択します。 次の図が表示されます。
タスク 10 とタスク 11 が互いに待機し、ブロックされるようになりました。 また、いくつかの新しいタスクがスケジュールされています。 スケジュールされたタスクは、コードで開始されたが、まだ実行されていないタスクです。 そのため、 その [場所 ] 列と [ スレッド割り当て] 列には、既定のメッセージが表示されるか、空になります。
タスク 5 は完了したため、表示されなくなります。 お使いのコンピューターでそうではなく、デッドロックが表示されない場合は、 F11 キーを押して 1 回ステップします。
タスク 3 とタスク 4 が互いに待機し、ブロックされるようになりました。 タスク 2 の子であり、現在スケジュールされている 5 つの新しいタスクもあります。 スケジュールされたタスクは、コードで開始されたが、まだ実行されていないタスクです。 そのため、 Location 列と Thread Assignment 列は空です。
[並列スタック] ウィンドウをもう一度表示します。 各ボックスのヘッダーには、スレッド ID と名前を示すヒントがあります。 [並列スタック] ウィンドウの [タスク] ビューに切り替えます。 次の図に示すように、ヘッダーにカーソルを合わせると、タスク ID と名前、およびタスクの状態が表示されます。
タスクは列ごとにグループ化できます。 [ タスク ] ウィンドウで、[ 状態 ] 列ヘッダーを右クリックし、[ 状態別にグループ化] を選択します。 次の図は、[ タスク] ウィンドウが状態別にグループ化されていることを示しています。
他の列でグループ化することもできます。 タスクをグループ化することで、タスクのサブセットに集中できます。 折りたたみ可能な各グループには、グループ化された項目の数があります。
確認する タスク ウィンドウの最後の機能は、タスクを右クリックしたときに表示されるショートカット メニューです。
ショートカット メニューには、タスクの状態に応じて、さまざまなコマンドが表示されます。 コマンドには、 コピー、 すべて選択、 16 進数表示、 タスクへの切り替え、 割り当てられたスレッドの固定、 この以外のすべてのスレッドの凍結、 割り当てられたスレッドの凍結解除、 フラグなどがあります。
タスクまたはタスクの基になるスレッドを固定することも、割り当てられたスレッドを除くすべてのスレッドを固定することもできます。 [タスク]ウィンドウで固定されたスレッドは、[スレッド]ウィンドウと同様に、青い一時停止アイコンで表されます。
概要
このチュートリアルでは、 並列タスク と 並列スタック デバッガー ウィンドウについて説明しました。 マルチスレッド コードを使用する実際のプロジェクトでは、これらのウィンドウを使用します。 C++、C#、または Visual Basic で記述された並列コードを調べることができます。