Compartilhar via


Instruções passo a passo: depurando um aplicativo paralelo

Este passo a passo descreve como usar as janelas Tarefas Paralelas e Pilhas Paralelas para depurar um aplicativo paralelo. Essas janelas ajudam a compreender e a verificar o comportamento em tempo de execução do código que usa Biblioteca de tarefas paralelas (TPL) ou Tempo de Execução de Simultaneidade. Este passo a passo fornece código de exemplo que tem pontos de interrupção internos. Após a interrupção do código, este passo a passo mostra como usar as janelas Tarefas Paralelas e Pilhas Paralelas para examiná-lo.

Este passo a passo ensina estas tarefas:

  • Como exibir as chamadas de pilhas de todos os threads em uma exibição.

  • Como exibir a lista de instâncias de System.Threading.Tasks.Task que são criadas no seu aplicativo.

  • Como exibir as chamadas de pilhas de tarefas reais em vez de threads.

  • Como navegar até o código a partir das janelas Tarefas Paralelas e Pilhas Paralelas.

  • Como as janelas lidam com a escala por agrupamento, zoom e outros recursos relacionados.

Pré-requisitos

Este passo a passo pressupõe que Apenas Meu Código está habilitado. No menu Ferramentas, clique em Opções, expanda o nó Depuração, selecione Geral e Habilitar Apenas Meu Código (Gerenciado Somente). Se você não definir esse recurso, ainda poderá usar este passo a passo, mas os resultados poderão ser diferentes das ilustrações.

Exemplo do C#

Se você usar o exemplo do C#, este passo a passo também pressuporá que o código externo está oculto. Para ativar ou desativar a exibição do código externo, clique com o botão direito do mouse no cabeçalho da tabela Nome da janela Pilha de Chamadas e marque ou desmarque Mostrar Código Externo. Se você não definir esse recurso, ainda poderá usar este passo a passo, mas os resultados poderão ser diferentes das ilustrações.

Exemplo do C++

Se você usar o exemplo do C++, poderá ignorar referências ao código externo neste tópico. O código externo aplica-se somente ao exemplo do C#.

Ilustrações

As ilustrações neste tópico foram gravadas em um computador de quatro núcleos executando o exemplo do C#. Embora você possa usar outras configurações para concluir este passo a passo, as ilustrações poderão diferir do que é exibido em seu computador.

Criando o projeto de exemplo

O código de exemplo neste passo a passo é para um aplicativo que não faça nada. O objetivo é apenas entender como usar as janelas de ferramenta para depurar um aplicativo paralelo.

Para criar o projeto de exemplo

  1. No Visual Studio, no menu Arquivo, aponte para Novo e clique em Projeto.

  2. No painel Modelos Instalados, selecione Visual C #, Visual Basic ou Visual C++. Para as linguagens gerenciadas, certifique-se de que .NET Framework 4 seja exibido na caixa da estrutura.

  3. Selecione Aplicativo de Console e clique em OK. Permaneça na configuração de depuração, que é o padrão.

  4. No projeto, abra o arquivo de código .cpp, .cs ou .vb. Exclua o conteúdo para criar um arquivo de código vazio.

  5. Cole o código a seguir para seu idioma escolhido no arquivo de código vazio.

Imports System
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Diagnostics

Module S

  Sub Main()

    pcount = Environment.ProcessorCount
    Console.WriteLine("Proc count = " + pcount.ToString())
    ThreadPool.SetMinThreads(4, -1)
    ThreadPool.SetMaxThreads(4, -1)

    t1 = New Task(AddressOf A, 1)
    t2 = New Task(AddressOf A, 2)
    t3 = New Task(AddressOf A, 3)
    t4 = New Task(AddressOf 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()
  End Sub 
  Sub A(ByVal o As Object)
    B(o)
  End Sub 
  Sub B(ByVal o As Object)
    C(o)
  End Sub 
  Sub C(ByVal o As Object)

    Dim temp As Integer = o

    Interlocked.Increment(aa)
    While (aa < 4)
    End While 

    If (temp = 1) Then 
      ' BP1 - all tasks in C
      Debugger.Break()
      waitFor1 = False 
    Else 
      While (waitFor1)
      End While 
    End If 
    Select Case temp
      Case 1
        D(o)
      Case 2
        F(o)
      Case 3, 4
        I(o)
      Case Else
        Debug.Assert(False, "fool")
    End Select 
  End Sub 
  Sub D(ByVal o As Object)
    E(o)
  End Sub 
  Sub E(ByVal o As Object)
    ' break here at the same time as H and K 
    While (bb < 2)
    End While 
    'BP2 - 1 in E, 2 in H, 3 in J, 4 in K
    Debugger.Break()
    Interlocked.Increment(bb)

    'after
    L(o)
  End Sub 
  Sub F(ByVal o As Object)
    G(o)
  End Sub 
  Sub G(ByVal o As Object)
    H(o)
  End Sub 
  Sub H(ByVal o As Object)
    ' break here at the same time as E and K
    Interlocked.Increment(bb)
    Monitor.Enter(mylock)
    While (bb < 3)
    End While
    Monitor.Exit(mylock)

    'after
    L(o)
  End Sub 
  Sub I(ByVal o As Object)
    J(o)
  End Sub 
  Sub J(ByVal o As Object)

    Dim temp2 As Integer = o

    Select Case temp2
      Case 3
        t4.Wait()
      Case 4
        K(o)
      Case Else
        Debug.Assert(False, "fool2")
    End Select 
  End Sub 
  Sub K(ByVal o As Object)
    ' break here at the same time as E and H
    Interlocked.Increment(bb)
    Monitor.Enter(mylock)
    While (bb < 3)
    End While
    Monitor.Exit(mylock)

    'after
    L(o)
  End Sub 
  Sub L(ByVal oo As Object)
    Dim temp3 As Integer = oo

    Select Case temp3
      Case 1
        M(oo)
      Case 2
        N(oo)
      Case 4
        O(oo)
      Case Else
        Debug.Assert(False, "fool3")
    End Select 
  End Sub 
  Sub M(ByVal o As Object)
    ' breaks here at the same time as N and Q
    Interlocked.Increment(cc)
    While (cc < 3)
    End While 

    'BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q
    Debugger.Break()
    Interlocked.Increment(cc)
    While (True)
      Thread.Sleep(500) '  for ever
    End While 
  End Sub 
  Sub N(ByVal o As Object)
    ' breaks here at the same time as M and Q
    Interlocked.Increment(cc)
    While (cc < 4)
    End While
    R(o)
  End Sub 
  Sub O(ByVal o As Object)
    Dim t5 As Task = Task.Factory.StartNew(AddressOf P, TaskCreationOptions.AttachedToParent)
    t5.Wait()
    R(o)
  End Sub 
  Sub P()
    Console.WriteLine("t5 runs " + Task.CurrentId.ToString())
    Q()
  End Sub 
  Sub Q()
    ' breaks here at the same time as N and M
    Interlocked.Increment(cc)
    While (cc < 4)
    End While 
    ' task 5 dies here freeing task 4 (its parent)
    Console.WriteLine("t5 dies " + Task.CurrentId.ToString())
    waitFor5 = False 
  End Sub 
  Sub R(ByVal o As Object)
    If (o = 2) Then 
      ' wait for task5 to die 
      While waitFor5
      End While 

      '//spin up all procs 
      Dim i As Integer 
      For i = 0 To pcount - 4 - 1

        Dim t As Task = Task.Factory.StartNew(Sub()
                                                While True 

                                                End While 
                                              End Sub)
        Console.WriteLine("Started task " + t.Id.ToString())
      Next

      Task.Factory.StartNew(AddressOf T, i + 1 + 5, TaskCreationOptions.AttachedToParent) ' //scheduled
      Task.Factory.StartNew(AddressOf T, i + 2 + 5, TaskCreationOptions.AttachedToParent) ' //scheduled
      Task.Factory.StartNew(AddressOf T, i + 3 + 5, TaskCreationOptions.AttachedToParent) ' //scheduled
      Task.Factory.StartNew(AddressOf T, i + 4 + 5, TaskCreationOptions.AttachedToParent) ' //scheduled
      Task.Factory.StartNew(AddressOf 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(o = 4)
      t3.Wait()
    End If 
  End Sub 
  Sub T(ByVal o As Object)
    Console.WriteLine("Scheduled run " + Task.CurrentId.ToString())
  End Sub 
  Private t1, t2, t3, t4 As Task
  Private aa As Integer = 0
  Private bb As Integer = 0
  Private cc As Integer = 0
  Private waitFor1 As Boolean = True 
  Private waitFor5 As Boolean = True 
  Private pcount As Integer 
  Private mylock As New S2()
End Module 

Public Class S2

End Class
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();
}
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <ppl.h>
#include <agents.h>
#include <stdio.h>
#include <concrtrm.h>
#include <vector>

CRITICAL_SECTION cs;

using namespace ::std;
using namespace ::std::tr1;
using namespace ::Concurrency;
task_group task4;
task_group task3;
task_group task2;

volatile long aa = 0;
volatile long bb = 0;
volatile long cc = 0;
static bool waitFor1 = true;
static bool waitFor5 = true;

#pragma optimize("", off)
void Spin()
{
   for(int i=0;i<50*50000;++i);
}
#pragma optimize("",on)

template<class Func>
class RunFunc
{
   Func& m_Func;
   int m_o;
public:
   RunFunc(Func func,int o):m_Func(func),m_o(o){

   };
   void operator()()const{
      m_Func(m_o);
   };
};

void T(int o)
{
   cout << "Scheduled run \n";

};

void R(int o)
{
   if (o == 2)
   {
      while (waitFor5) { ;}
      Spin();

      //use up all processors but 4 by scheduling 4 non-terminating tasks. 
      int numProcsToBurn = GetProcessorCount() - 4;
      int i;
      vector<call<int>*> tasks;
      for (i = 0; i <  numProcsToBurn; i++)
      {
         tasks.push_back(new call<int>([](int i){while(true)Spin();}));
         asend(tasks[i],1);
         cout << "Started task  \n";
      }

      task_handle<RunFunc<decltype(T)>> t6(RunFunc<decltype(T)>(T,i + 1 + 5));
      task_handle<RunFunc<decltype(T)>> t7(RunFunc<decltype(T)>(T,i + 2 + 5));
      task_handle<RunFunc<decltype(T)>> t8(RunFunc<decltype(T)>(T,i + 3 + 5));
      task_handle<RunFunc<decltype(T)>> t9(RunFunc<decltype(T)>(T,i + 4 + 5));
      task_handle<RunFunc<decltype(T)>> t10(RunFunc<decltype(T)>(T,i + 5 + 5));
      task2.run(t6);
      task2.run(t7);
      task2.run(t8);
      task2.run(t9);
      task2.run(t10);

      //BP4 - 1 in M, 2 in R, 3 in J, 4 in R, 5 died   
      DebugBreak();
   }
   else
   {
      if (o!=4)
         throw;

      task3.wait();      
   }
};

void Q()
{
   // breaks here at the same time as N and M
   InterlockedIncrement(& cc);
   while (cc < 4)
   {
      ;
   }
   // task 5 dies here freeing task 4 (its parent)
   cout << "t5 dies\n";
   waitFor5 = false;
};

void P()
{
   cout << "t5 runs\n";
   Q();
};

void O(int o)
{   
   task_group t5;
   t5.run(&P);
   t5.wait();
   R(o);
};

void N(int o)
{
   // breaks here at the same time as M and Q
   InterlockedIncrement(&cc);
   while (cc < 4)
   {
      ;
   }
   R(o);
};

void M(int o)
{
   // breaks here at the same time as N and Q
   InterlockedIncrement(&cc);
   while (cc < 3)
   {
      ;
   }
   //BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q
   DebugBreak();
   InterlockedIncrement(&cc);
   while (true)
      Sleep(500); // for ever
};

void L(int oo)
{
   int temp3 = oo;

   switch (temp3)
   {
   case 1:
      M(oo);
      break;
   case 2:
      N(oo);
      break;
   case 4:
      O(oo);
      break;
   default:
      throw; //fool3 
      break;
   }
}
void K(int o)
{
   // break here at the same time as E and H
   InterlockedIncrement(&bb);
   EnterCriticalSection(&cs);
   while (bb < 3)
   {
      ;
   }
   LeaveCriticalSection(&cs);
   Spin();

   //after
   L(o);
}
void J(int o)
{
   int temp2 = o;

   switch (temp2)
   {
   case 3:
      task4.wait();
      break;
   case 4:
      K(o);
      break;
   default:
      throw; //fool2 
      break;
   }
}
static void I(int o)
{
   J(o);
}
static void H(int o)
{
   // break here at the same time as E and K
   InterlockedIncrement(&bb);
   EnterCriticalSection(&cs);
   while (bb < 3)
   {
      ;
   }
   LeaveCriticalSection(&cs);
   Spin();

   //after
   L(o);
}
static void G(int o)
{
   H(o);
}
static void F(int o)
{
   G(o);
}

static void E(int 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   
   Spin(); // for native case only
   DebugBreak();
   InterlockedIncrement(&bb);

   //after
   L(o);

}

static void D(int o)
{
   E(o);
}

static void C(int o)
{
   int temp = o;

   InterlockedIncrement(&aa);
   while (aa < 4)
   {
      ;
   }

   if (temp == 1)
   {
      // BP1 - all tasks in C   
      DebugBreak();
      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:
      throw; //fool 
      break;
   }
}
static void B(int o)
{
   C(o);
}

void A(int o)
{
   B(o);
}
int main()
{
   InitializeCriticalSection(&cs);

   task_group tasks;
   task_handle<RunFunc<decltype(A)>> t1(RunFunc<decltype(A)>(A,1));
   tasks.run(t1);
   task_handle<RunFunc<decltype(A)>> t2(RunFunc<decltype(A)>(A,2));
   task2.run(t2);
   task_handle<RunFunc<decltype(A)>> t3(RunFunc<decltype(A)>(A,3));
   task3.run(t3);
   task_handle<RunFunc<decltype(A)>> t4(RunFunc<decltype(A)>(A,4));
   task4.run(t4);

   getchar();
   return 1;
}
  1. No menu Arquivo, clique em Salvar Tudo.

  2. No menu Compilação, clique em Recompilar Solução.

    Observe que há quatro chamadas a Debugger.Break (DebugBreak no exemplo do C++). Em virtude disso, você não precisa inserir pontos de interrupção; a execução do aplicativo causará sua interrupção no depurador até quatro vezes.

Usando a janela Pilhas Paralelas: exibição de Threads

No menu Depuração, clique em Iniciar Depuração. Aguarde até que o primeiro ponto de interrupção seja atingido.

Para exibir a pilha de chamadas de um único thread

  1. No menu Depurar, aponte para Windows e clique em Threads. Encaixe a janela Threads na parte inferior do Visual Studio.

  2. No menu Depurar, aponte para Windows e clique em Pilha de Chamadas. Encaixe a janela Pilha de Chamadas na parte inferior do Visual Studio.

  3. Clique duas vezes em um thread na janela Threads para torná-lo atual. Os threads atuais têm uma seta amarela. Quando você altera o thread atual, a pilha de chamadas é exibida na janela Pilha de Chamadas.

Para examinar a janela Pilhas Paralelas

  • No menu Depurar, aponte para Windows e clique em Pilhas Paralelas. Verifique se Threads está selecionado na caixa no canto superior esquerdo.

    Usando a janela Pilhas Paralelas , você pode exibir várias pilhas de chamadas ao mesmo tempo em uma exibição. A ilustração a seguir mostra a janela Pilhas Paralelas acima da janela Pilha de Chamadas.

    Exibição de threads na janela pilhas paralelas

    A pilha de chamadas do thread principal é exibida em uma caixa e as pilhas de chamadas dos outros quatro threads são agrupadas em outra caixa. Quatro threads são agrupados porque os quadros de pilhas compartilham os mesmos contextos do método; isto é, estão nos mesmos métodos: A, B, e C. Para exibir as IDs de thread e os nomes dos threads que compartilham a mesma caixa, passe o mouse sobre o cabeçalho (4 Threads). O thread atual é exibido em negrito, como mostra a ilustração a seguir.

    Dica de ferramenta que mostra os nomes e IDs de thread

    A seta amarela indica o quadro de pilhas do thread atual. Para obter mais informações, passe o mouse sobre ela

    Dica de ferramenta no quadro de pilha ativa

    Você pode definir o nível de detalhes que deseja mostrar dos registros de ativação (Nomes de Módulo, Tipos de Parâmetro, Nomes de Parâmetros, Valores de Parâmetro, Números de Linha e Deslocamentos de Byte) clicando com o botão direito do mouse na janela Pilha de Chamadas.

    Um realce azul ao redor de uma caixa indica que o thread atual é parte dessa caixa. O thread atual também é indicado pelo registro de ativação em negrito na dica de ferramenta. Se você clicar duas vezes no thread principal na janela Threads, poderá observar que o realce azul na janela Pilhas Paralelas se move também.

    Thread principal realçado na janela pilhas paralelas

Para retomar a execução até o segundo ponto de interrupção

  • Para retomar a execução até atingir o segundo ponto de interrupção, no menu Depurar, clique em Continuar. A ilustração a seguir mostra a árvore do thread no segundo ponto de interrupção.

    Janela pilhas paralelas que mostra muitas ramificações

    No primeiro ponto de interrupção, quatro threads foram de S.A para S.B até o método S.C. Essas informações ainda estão visíveis na janela Pilhas Paralelas, mas os quatro threads progrediram mais. Um deles continuou até S.D e depois S.E. Outro continuou até S.F, S.G e S.H. Dois outros continuaram até S.I e S.J, e um deles foi até S.K e o outro continuou até o código externo de não usuário.

    Você pode passar o mouse sobre o cabeçalho da caixa, por exemplo, 1 Thread ou 2 Threads, para ver as IDs dos threads. Você pode passar o mouse sobre registros de ativação para consultar as IDs de thread e outros detalhes do registro. O realce azul indica o thread atual e a seta amarela indica o registro de ativação ativo do thread atual.

    O ícone de pano-threads (sobrepondo linhas onduladas azuis e vermelhas) indica os registros de ativação ativos dos threads não atuais. Na janela Pilha de Chamadas, clique duas vezes em S.B para trocar de registros. A janela Pilhas Paralelas indica o registro de ativação atual do thread atual usando um ícone de seta curva verde.

    Na janela Threads, alterne entre threads e observe que a exibição na janela Pilhas Paralelas é atualizada.

    Você pode alternar para outro thread ou para outro registro de outro thread, usando o menu de atalho na janela Pilhas Paralelas. Por exemplo, clique com o botão direito do mouse em S.J, aponte para Alternar para Quadro e clique em um comando.

    Caminho de pilhas paralelas de execução

    Clique com o botão direito do mouse em S.C e aponte para Alternar para Quadro. Um dos comandos tem uma marca de seleção que indica o registro de ativação do thread atual. Você pode alternar para esse registro do mesmo thread (apenas a seta verde se moverá) ou pode alternar para o outro thread (o realce azul também se moverá). A ilustração a seguir mostra o submenu.

    Menu pilhas com 2 opções em C, embora J seja o atual

    Quando um contexto de método é associado a apenas um registro de ativação, o cabeçalho da caixa exibe 1 Thread e você pode alternar clicando duas vezes nele. Se você clicar duas vezes em um contexto de método que tenha mais de 1 registro associado a ele, o menu será exibido automaticamente. Enquanto você passa o mouse sobre os contextos de método, observe o triângulo preto no lado direito. Um clique nesse triângulo também exibe o menu de atalho.

    Para os aplicativos grandes que têm muitos threads, talvez seja conveniente se concentrar apenas em um subconjunto de threads. A janela Pilhas Paralelas pode exibir pilhas de chamadas apenas para threads sinalizados. Na barra de ferramentas, clique no botão Mostrar Somente Sinalizados ao lado da caixa de listagem.

    Janela pilhas paralelas vazias e dica de ferramenta

    Em seguida, na janela Threads, sinalize threads individualmente para ver como as respectivas pilhas de chamadas aparecem na janela Pilhas Paralelas. Para sinalizar threads, use o menu de atalho ou a primeira célula de um thread. Clique no botão de barra de ferramentas Mostrar Somente Sinalizados novamente para mostrar todos os threads.

Para retomar a execução até o terceiro ponto de interrupção

  1. Para retomar a execução até atingir o terceiro ponto de interrupção, no menu Depurar, clique em Continuar.

    Quando vários threads estão no mesmo método, mas o método não estava no início da pilha de chamadas, ele é exibido em caixas diferentes. Um exemplo no ponto de interrupção atual é S.L, que tem três threads e aparece em três caixas. Clique duas vezes em S.L.

    Caminho de execução na janela pilhas paralelas

    Observe que S.L está em negrito nas outras duas caixas para que você possa ver onde mais ele aparece. Para saber quais registros chamam S.L e quais registros ele chama, clique no botão Ativar/Desativar Modo de Exibição do Método na barra de ferramentas. A ilustração a seguir mostra o modo de exibição do método da janela Pilhas Paralelas .

    Exibição do método na janela pilhas paralelas

    Observe como o diagrama girou no método selecionado e o posicionou em sua própria caixa no meio da exibição. Os receptores e os chamadores aparecem nas partes superior e inferior. Clique no botão Ativar/Desativar Modo de Exibição do Método novamente para sair desse modo.

    O menu de atalho da janela Pilhas Paralelas também tem os seguintes itens.

    • Exibição Hexadecimal alterna os números decimais e hexadecimais nas dicas de ferramenta.

    • Informações de Carregamento de Símbolos e Configurações de Símbolo abrem as respectivas caixas de diálogo.

    • Ir para Código-Fonte e Ir para Desmontagem navegam no editor até o método selecionado.

    • Mostrar Código Externo exibe todos os registros mesmo se não estiverem no código do usuário. Tente verificar o diagrama se expande para acomodar os registros adicionais (que podem ser escurecidos porque você não tem símbolos para eles).

    Quando houver diagramas grandes e você for para o próximo ponto de interrupção, talvez seja conveniente que o modo de exibição role até o registro de ativação ativo do thread atual; isto é, o thread que atingiu o ponto de interrupção primeiro. Na janela Pilhas Paralelas, verifique se o botão Autorrolagem para Quadro de Pilha Atual na barra de ferramentas está ativo.

    Autorrolagem na janela pilhas paralelas

  2. Antes de continuar, na janela Pilhas Paralelas, role até a extrema esquerda e até o fim.

Para retomar a execução até o quarto ponto de interrupção

  1. Para retomar a execução até atingir o quarto ponto de interrupção, no menu Depurar, clique em Continuar.

    Observe como o modo de exibição rolou automaticamente até o local certo. Alterne os threads na janela Threads ou alterne registros de ativação na janela Pilha de Chamadas e observe como o modo de exibição rola automaticamente sempre até o registro correto. Desative a opção Autorrolagem até Quadro de Ferramenta atual e veja a diferença.

    O Modo de Exibição Vista de Pássaro também é útil com diagramas grandes na janela Pilhas Paralelas. Você pode ver o Modo de Exibição de Vista de Pássaro clicando no botão entre as barras de rolagem no canto inferior direito da janela, como mostra a ilustração a seguir.

    Visão geral na janela pilhas paralelas

    Você pode mover o retângulo para ver uma rápida panorâmica do diagrama.

    Outra maneira de mover o diagrama em qualquer direção é clicar em uma área em branco do diagrama e arrastá-la até onde desejar.

    Para ampliar ou reduzir o diagrama, mantenha pressionada a tecla CTRL enquanto move a roda do mouse. Como alternativa, clique no botão Aplicar Zoom na barra de ferramentas e use a ferramenta Zoom.

    Pilhas de zoom na janela pilhas paralelas

    Você também pode exibir as pilhas na direção de cima para baixo em vez de baixo para cima, clicando no menu Ferramentas e em Opções e selecionando ou desmarcando a opção no nó Depuração.

  2. Antes de continuar, no menu Depurar, clique em Parar Depuração para terminar a execução.

Usando a janela Tarefas Paralelas e o Modo de Exibição Tarefas na janela Pilhas Paralelas

Recomendamos que você conclua os procedimentos anteriores antes de continuar.

Para reiniciar o aplicativo até atingir o primeiro ponto de interrupção

  1. No menu Depurar, clique em Iniciar Depuração e aguarde até que o primeiro ponto de interrupção seja atingido.

  2. No menu Depurar, aponte para Windows e clique em Threads. Encaixe a janela Threads na parte inferior do Visual Studio.

  3. No menu Depurar, aponte para Windows e clique em Pilha de Chamadas. Encaixe a janela Pilha de Chamadas na parte inferior do Visual Studio.

  4. Clique duas vezes em um thread na janela Threads para torná-lo atual. Os threads atuais têm a seta amarela. Quando você altera o thread atual, as outras janelas são atualizadas. Em seguida, examinaremos tarefas.

  5. No menu Depurar, aponte para Windows e clique em Tarefas Paralelas. A ilustração a seguir mostra a janela Tarefas Paralelas.

    Quatro tarefas em execução na janela tarefas paralelas

    Para cada tarefa em execução, você pode ler a ID, que é retornada pela propriedade do mesmo nome, a ID e o nome do thread que a executa, seu local (passar o mouse sobre ele exibe uma dica de ferramenta que tem a pilha de chamadas inteira). Além disso, na coluna Tarefa, você pode ver o método que foi passado na tarefa; em outras palavras, o ponto de partida.

    Você pode classificar qualquer coluna. Observe o glifo de classificação que indica a coluna e a direção de classificação. Também é possível reorganizar as colunas arrastando-as para a esquerda ou a direita.

    A seta amarela indica a tarefa atual. Você pode alternar tarefas clicando duas vezes em uma tarefa ou usando o menu de atalho. Quando você alterna tarefas, o thread subjacente se torna o atual e as outras janelas são atualizadas.

    Quando você alterna manualmente de uma tarefa para outra, a seta amarela se move, mas uma seta branca ainda mostra a tarefa que causou a interrupção do depurador.

Para retomar a execução até o segundo ponto de interrupção

  • Para retomar a execução até atingir o segundo ponto de interrupção, no menu Depurar, clique em Continuar.

    Anteriormente, a coluna Status mostrava todas as tarefas como Em Execução, mas agora duas tarefas estão Aguardando. As tarefas podem ser bloqueadas por diversos motivos diferentes. Na coluna Status, passe o mouse sobre uma tarefa em espera para saber porque ela está bloqueada. Por exemplo, na ilustração, a tarefa 3 está aguardando a tarefa 4.

    Duas tarefas em espera na janela tarefas paralelas

    A tarefa 4, por sua vez, está aguardando um monitor de propriedade do thread atribuído à tarefa 2.

    Aguardando a dica de ferramenta na janela de tarefas e tarefa

    Você pode sinalizar uma tarefa clicando no sinalizador na primeira coluna da janela Tarefas Paralelas.

    Você pode usar a sinalização para controlar tarefas entre pontos de interrupção diferentes na mesma sessão de depuração ou para filtrar as tarefas cujas pilhas de chamadas são mostradas na janela Pilhas Paralelas.

    Quando você usou a janela Pilhas Paralelas antes, exibiu os threads do aplicativo. Exiba a janela Pilhas Paralelas novamente, mas desta vez, exiba as tarefas do aplicativo. Para fazer isso, selecione Tarefas na caixa na parte superior esquerda. A ilustração a seguir mostra o Modo de Exibição de Tarefas.

    Exibição de threads na janela pilhas paralelas

    Os threads que não estão executando tarefas no momento não são mostrados no Modo de Exibição de Tarefas da janela Pilhas Paralelas. Além disso, para os threads que executam tarefas, alguns registros de ativação que não são relevantes para tarefas são filtrados das partes superior e inferior da pilha.

    Exiba a janela Tarefas Paralelas novamente. Clique com o botão direito do mouse em qualquer cabeçalho de coluna para ver um menu de atalho da coluna.

    Menu de exibição de atalho na janela tarefas paralelas

    Você pode usar o menu de atalho para adicionar ou remover colunas. Por exemplo, a coluna Appdomain não está selecionada; consequentemente, não é exibida na lista. Clique em Pai. A coluna Pai aparece sem valores para todas as quatro tarefas.

Para retomar a execução até o terceiro ponto de interrupção

  • Para retomar a execução até atingir o terceiro ponto de interrupção, no menu Depurar, clique em Continuar.

    Uma nova tarefa, tarefa 5, está sendo executada e a tarefa 4 está aguardando. Você pode ver porque passando o cursor do mouse sobre a tarefa em espera na janela Status. Na coluna Pai , observe que a tarefa 4 é o pai da tarefa 5.

    Para visualizar melhor a relação pai-filho, clique com o botão direito do mouse no cabeçalho da coluna Pai e clique em Modo de Exibição Pai-Filho. Você verá a ilustração a seguir.

    Exibição de pai-filho na janela tarefas paralelas

    Observe que as tarefas 4 e 5 estão em execução no mesmo thread. Essas informações não são exibidas na janela Threads; sua exibição é outro benefício da janela Tarefas Paralelas. Para confirmar isso, exiba a janela Pilhas Paralelas. Verifique se você está exibindo Tarefas. Localize as tarefas 4 e 5 clicando duas vezes nelas na janela Tarefas Paralelas. Quando fizer isso, o realce azul na janela Pilhas Paralelas será atualizado. Você também pode localizar as tarefas 4 e 5 revisando as dicas de ferramenta na janela Pilhas Paralelas.

    Modo de exibição de tarefas na janela pilhas paralelas

    Na janela Pilhas Paralelas, clique com o botão direito do mouse em S.P e clique em Ir para Thread. A janela alterna para o Modo de Exibição de Threads e o registro correspondente está na exibição. Você pode ver as duas tarefas no mesmo thread.

    Thread realçado na exibição de threads

    Esse é outro benefício do Modo de Exibição de Tarefas na janela Pilhas Paralelas, comparado com a janela Threads.

Para retomar a execução até o quarto ponto de interrupção

  • Para retomar a execução até atingir o terceiro ponto de interrupção, no menu Depurar, clique em Continuar. Clique no cabeçalho de coluna ID para classificar por ID. Você verá a ilustração a seguir.

    Quatro estados na janela pilhas paralelas de tarefas

    Como a tarefa 5 foi concluída, ele não é mais exibida. Se esse não for o caso no seu computador e o deadlock não for mostrado, volte uma etapa pressionando F11.

    As tarefas 3 e 4 estão aguardando uma pela outra e estão bloqueadas. Também há 5 novas tarefas que são filhos da tarefa 2 e estão agendadas agora. As tarefas agendadas são aquelas iniciadas no código mas que ainda não foram executadas. Consequentemente, suas colunas Local e Atribuição de Thread estão vazias.

    Exiba a janela Pilhas Paralelas novamente. O cabeçalho de cada caixa tem uma dica de ferramenta que mostra as IDs e os nomes de thread. Alterne para o Modo de Exibição de Tarefas na janela Pilhas Paralelas. Passe o mouse sobre um cabeçalho para ver o nome, a ID e o status da tarefa, como mostra a ilustração a seguir.

    Dica de ferramenta de cabeçalho na janela pilhas paralelas

    Você pode agrupar as tarefas por coluna. Na janela Tarefas Paralelas, clique com o botão direito do mouse no cabeçalho da coluna Status e clique em Agrupar por Status. A ilustração a seguir mostra a janela Tarefas Paralelas agrupada por status.

    Tarefas agrupadas na janela tarefas paralelas

    Também é possível agrupar por qualquer outra coluna. Agrupando tarefas, você pode se concentrar em um subconjunto de tarefas. Cada grupo recolhível tem uma contagem de itens que são agrupados. Você também pode sinalizar rapidamente todos os itens no grupo clicando no botão Sinalizar à direita do botão Recolher.

    Pilhas agrupadas na janela pilhas paralelas

    O último recurso da janela Tarefas Paralelas que deve ser examinado é o menu de atalho exibido quando você clica com o botão direito do mouse em uma tarefa.

    Menu de atalho na janela tarefas paralelas

    O menu de atalho exibe comandos diferentes, dependendo do status da tarefa. Os comandos podem incluir Copiar, Selecionar Tudo, Exibição Hexadecimal, Alternar para Tarefas, Congelar Thread Atribuído, Congelar Todos os Threads Exceto Este, Descongelar Thread Atribuído e Sinalizar.

    Você pode congelar o thread subjacente de uma tarefa, ou tarefas, ou pode congelar todos os threads exceto o atribuído. Um thread congelado é representado na janela Tarefas Paralelas como estiver na janela Threads, por um ícone azul de pausa.

Resumo

Este passo a passo demonstrou as janelas do depurador Tarefas Paralelas e Pilhas Paralelas. Use essas janelas em projetos reais que utilizam código multi-threaded. Você pode examinar o código paralelo escrito no C++, no C# ou no Visual Basic.

Consulte também

Tarefas

Instruções passo a passo: depurando um aplicativo paralelo

Conceitos

Tempo de Execução de Simultaneidade

Usando a janela Pilhas Paralelas

Usando a janela Tarefas

Outros recursos

Mapa de depurador

Depurando código gerenciado

Programação paralela no .NET Framework