Compartir a través de


Tutorial: Depuración de una aplicación paralela en Visual Studio (C#, Visual Basic, C++)

En este tutorial se muestra cómo usar las ventanas Tareas paralelas y Pilas paralelas para depurar una aplicación paralela. Estas ventanas le ayudan a comprender y comprobar el comportamiento en tiempo de ejecución del código que usa la biblioteca paralela de tareas (TPL) o el runtime de simultaneidad. En este tutorial se proporciona código de ejemplo que tiene puntos de interrupción integrados. Una vez que se interrumpe el código, el tutorial muestra cómo usar las ventanas Tareas paralelas y Pilas paralelas para examinarlo.

En este tutorial se enseñan estas tareas:

  • Cómo ver las pilas de llamadas de todos los subprocesos en una sola vista.

  • Cómo ver la lista de System.Threading.Tasks.Task instancias que se crean en la aplicación.

  • Cómo ver las pilas de invocación reales de tareas en vez de subprocesos.

  • Cómo navegar hacia el código desde las ventanas de Tareas paralelas y Pilas paralelas.

  • Cómo las ventanas se enfrentan a la escala mediante la agrupación, el zoom y otras características relacionadas.

Prerrequisitos

En este tutorial se supone que Solo mi código está habilitado (está habilitado de forma predeterminada en versiones más recientes de Visual Studio). En el menú Herramientas, seleccione Opciones, expanda el nodo Depuración, seleccione General y, a continuación, seleccione Habilitar solo mi código (solo administrado). Si no establece esta característica, puede seguir usando este tutorial, pero los resultados pueden diferir de las ilustraciones.

Ejemplo de C#

Si usa el ejemplo de C#, en este tutorial también se supone que el código externo está oculto. Para alternar si se muestra código externo, haga clic con el botón derecho en el encabezado de la tabla Nombre de la ventana Pila de llamadas y, a continuación, seleccione o desactive Mostrar código externo. Si no establece esta característica, puede seguir usando este tutorial, pero los resultados pueden diferir de las ilustraciones.

Ejemplo de C++

Si usa el ejemplo de C++, puede omitir las referencias al código externo de este artículo. El código externo solo se aplica al ejemplo de C#.

Ilustraciones

Las ilustraciones de este artículo se registran en un equipo de cuatro núcleos que ejecuta el ejemplo de C#. Aunque puede usar otras configuraciones para completar esta guía, las ilustraciones pueden diferir de lo que se muestra en su ordenador.

Creación del proyecto de ejemplo

El código de ejemplo de este tutorial es para una aplicación que no hace nada. El propósito del ejercicio es comprender cómo usar las ventanas de herramientas para depurar una aplicación paralela.

  1. Abra Visual Studio y cree un proyecto.

    Si la ventana de inicio no está abierta, elija Archivo>Ventana de inicio.

    En la ventana de inicio, elija Nuevo proyecto.

    En la ventana de inicio, elija Crear un nuevo proyecto.

    En la ventana Crear un nuevo proyecto, escriba console en el cuadro de búsqueda. A continuación, elija C#, C++o Visual Basic en la lista Lenguaje y, a continuación, elija Windows en la lista Plataforma.

    Después de aplicar los filtros de lenguaje y plataforma, elija la aplicación de consola para .NET Core o C++y, a continuación, elija Siguiente.

    Nota:

    Si no ve la plantilla correcta, vaya a Herramientas>Obtener herramientas y características..., que abre el Instalador de Visual Studio. Elija la carga de trabajo Desarrollo de escritorio de .NET o Desarrollo de escritorio con C++ y, a continuación, elija Modificar.

    En la ventana Configurar el nuevo proyecto , escriba un nombre o use el nombre predeterminado en el cuadro Nombre del proyecto. A continuación, elija Siguiente o Crear, la opción que esté disponible.

    En .NET Core, elija la plataforma de destino recomendada o .NET 8 y, después, elija Crear.

    Aparece un nuevo proyecto de consola. Una vez creado el proyecto, aparece un archivo de origen.

  2. Abra el archivo de código .cpp, .cs o .vb en el proyecto. Elimine su contenido para crear un archivo de código vacío.

  3. Pegue el código siguiente para el idioma elegido en el archivo de código vacío.

    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();
    }
    

Después de actualizar el archivo de código, guarde los cambios y compile la solución.

  1. En el menú Archivo, seleccione Guardar todo.

  2. En el menú Compilar , seleccione Recompilar solución.

Observe que hay cuatro llamadas a Debugger.Break (DebugBreak en el ejemplo de C++). Por lo tanto, no es necesario insertar puntos de interrupción. Simplemente al ejecutar la aplicación, genera una interrupción en el depurador hasta cuatro veces.

Usar la ventana Pilas Paralelas: Vista de subprocesos

Para comenzar, en el menú Depurar , seleccione Iniciar depuración. Espere hasta que se alcance el primer punto de interrupción.

Visualización de la pila de llamadas de un único subproceso

  1. En el menú Depurar , seleccione Windows y, a continuación, seleccione Subprocesos. Acoplar la ventana Subprocesos en la parte inferior de Visual Studio.

  2. En el menú Depurar , seleccione Windows y, a continuación, seleccione Pila de llamadas. Acopla la ventana Pila de llamadas en la parte inferior de Visual Studio.

  3. Haga doble clic en un subproceso en la ventana Subprocesos para establecerlo como actual. Los hilos actuales tienen una flecha amarilla. Al cambiar el subproceso actual, su pila de llamadas se muestra en la ventana Pila de llamadas .

Examinar la ventana de pilas paralelas

En el menú Depurar , seleccione Windows y, a continuación, seleccione Pilas paralelas. Asegúrese de que Subprocesos está seleccionado en el cuadro de la esquina superior izquierda.

Mediante la ventana Pilas paralelas , puede ver varias pilas de llamadas al mismo tiempo en una vista. En la ilustración siguiente se muestra la ventana Pilas paralelas encima de la ventana Pila de llamadas .

Captura de pantalla de la vista Subprocesos en la ventana Pilas paralelas.

Vista de subprocesos en la ventana Pilas paralelas

La pila de llamadas del subproceso Main aparece en un cuadro y las pilas de llamadas de los otros cuatro subprocesos se agrupan en otro cuadro. Se agrupan cuatro subprocesos porque sus marcos de pila comparten los mismos contextos de método; es decir, están en los mismos métodos: A, By C. Para ver los identificadores de subproceso y los nombres de los subprocesos que comparten el mismo cuadro, mantenga el puntero sobre el cuadro con el encabezado ([#] Subprocesos). El hilo actual se muestra en negrita.

Captura de pantalla del tooltip que muestra identificadores y nombres de subprocesos.

Información emergente que muestra los identificadores de subprocesos y los nombres

La flecha amarilla indica el marco de pila activo del hilo actual.

Puede establecer la cantidad de detalles que se van a mostrar para los marcos de pila (nombres de módulo, tipos de parámetros, nombres de parámetros, valores de parámetro, números de línea y desplazamientos de bytes) haciendo clic con el botón derecho en la ventana Pila de llamadas.

Un resaltado azul alrededor de un cuadro indica que el hilo actual forma parte de ese cuadro. El subproceso actual también se indica mediante el marco de pila en negrita de la información sobre herramientas. Si hace doble clic en el subproceso principal de la ventana Subprocesos, puede observar que la flecha de resaltado de la ventana Pilas paralelas se mueve en consecuencia.

Captura de pantalla del subproceso principal resaltado en la ventana Pilas paralelas.

Subproceso principal resaltado en la ventana Pilas paralelas

Reanudación de la ejecución hasta el segundo punto de interrupción

Para reanudar la ejecución hasta que se alcance el segundo punto de interrupción, en el menú Depurar , seleccione Continuar. En la ilustración siguiente se muestra el árbol de subprocesos en el segundo punto de interrupción.

Captura de pantalla de la ventana Stacks paralelas que muestra múltiples ramas.

Ventana de pilas paralelas que muestra muchas ramas

En el primer punto de interrupción, cuatro subprocesos pasaron de métodos S.A a S.B a S.C. Esa información sigue siendo visible en la ventana Pilas paralelas, pero los cuatro subprocesos han avanzado aún más. Uno de ellos continuó hacia S.D y luego S.E. Otro continuó hacia S.F, S.G y S.H. Otros dos continuaron hacia S.I y S.J, y desde allí uno de ellos fue a S.K y el otro continuó hasta Nonuser External Code.

Puede pasar el ratón sobre marcos de pila para ver los identificadores de subprocesos y otros detalles del marco. El resaltado azul indica el subproceso actual y la flecha amarilla indica el cuadro de pila activo del subproceso actual.

Puede mantener el puntero sobre el encabezado de cuadro, por ejemplo, 1 subproceso o 2 subprocesos, para ver los identificadores de los subprocesos. Puede pasar el ratón sobre marcos de pila para ver los identificadores de subprocesos y otros detalles del marco. El resaltado azul indica el subproceso actual y la flecha amarilla indica el cuadro de pila activo del subproceso actual.

El icono de hilos de tela (líneas intercaladas) indica los marcos de pila activos de los subprocesos nocurrentes. En la ventana Pila de llamadas, haga doble clic en S.B para cambiar de contexto. La ventana Pilas paralelas indica el marco de pila actual del subproceso actual mediante un icono de flecha curvada.

Nota:

Para obtener una descripción de todos los iconos de la ventana Pilas paralelas, consulte Uso de la ventana Pilas paralelas.

En la ventana Subprocesos , cambie entre subprocesos y observe que la vista de la ventana Pilas paralelas se actualiza.

Puede cambiar a otro subproceso o a otro marco de otro subproceso mediante el menú contextual de la ventana Pilas paralelas . Por ejemplo, haga clic con el botón derecho en S.J, seleccione Cambiar a marco y, a continuación, seleccione un comando.

Captura de pantalla de la ruta de acceso de las pilas paralelas de ejecución.

Ruta de acceso de la ejecución de pilas paralelas PDB_Walkthrough_2B

Haga clic con el botón derecho en S.C y seleccione Cambiar a marco. Uno de los comandos tiene una marca de verificación que indica el marco de pila del hilo actual. Puede cambiar a ese marco del mismo subproceso (solo se mueve la flecha curvada) o puede cambiar al otro subproceso (el resaltado azul también se mueve). En la ilustración siguiente se muestra el submenú.

Captura de pantalla del menú Pilas con 2 opciones en C mientras J está activo.

Menú de pilas con 2 opciones en C mientras J está activo

Cuando un contexto de método está asociado a solo un marco de pila, el encabezado de cuadro muestra 1 subproceso y puede cambiar a él haciendo doble clic. Si hace doble clic en un contexto de método que tiene más de 1 marco asociado, el menú aparece automáticamente. Al mantener el puntero sobre los contextos del método, observe el triángulo negro a la derecha. Al hacer clic en ese triángulo también se muestra el menú contextual.

Para aplicaciones de gran tamaño que tienen muchos subprocesos, es posible que quiera centrarse en solo un subconjunto de subprocesos. La ventana Pilas paralelas solo puede mostrar pilas de llamadas para subprocesos marcados. Para marcar subprocesos, use el menú contextual o la primera celda de un subproceso.

En la barra de herramientas, seleccione el botón Mostrar solo marcado junto al cuadro de lista.

Captura de pantalla de la ventana Pilas Paralelas y la información sobre herramientas.

Ventana de pilas paralelas y de información sobre herramientas, PDB_Walkthrough_3A

Ahora, solo se muestran los hilos marcados en la ventana Pilas paralelas.

Reanudación de la ejecución hasta el tercer punto de interrupción

  1. Para reanudar la ejecución hasta que se alcance el tercer punto de interrupción, en el menú Depurar , seleccione Continuar.

    Cuando varios subprocesos están en el mismo método, pero el método no estaba al principio de la pila de llamadas, el método aparece en cuadros diferentes. Un ejemplo en el punto de inflexión actual es S.L, que tiene tres subprocesos y aparece en tres cajas. Haga doble clic en S.L.

    Captura de pantalla de la ruta de acceso de ejecución en la ventana Pilas paralelas.

    Ruta de acceso de ejecución en la ventana Pilas paralelas

    Observe que S.L está en negrita en los otros dos cuadros para que pueda ver dónde más aparece. Si desea ver qué fotogramas llaman a S.L y a cuáles llama S.L, seleccione el botón Cambiar a vista de método en la barra de herramientas. En la ilustración siguiente se muestra la vista de método de la ventana Pilas paralelas .

    Captura de pantalla de la vista Método en la ventana Pilas Paralelas.

    Vista método en la ventana Pilas paralelas

    Observe cómo el diagrama se centra pivotando en el método seleccionado y lo ubica en su caja propia en el centro de la vista. Las llamadas y autores de llamadas aparecen en la parte superior e inferior, respectivamente. Seleccione nuevamente el botón Alternar vista de método para salir de este modo.

    El menú contextual de la ventana Pilas paralelas también tiene los siguientes elementos.

    • Visualización hexadecimal alterna los números en los tooltips entre decimal y hexadecimal.

    • Configuración de símbolos abre los cuadros de diálogo respectivos.

    • Mostrar hilos en el código alterna la visualización de marcadores de hilos, indicando su ubicación en el código fuente.

    • Mostrar código externo muestra todos los fotogramas aunque no estén en el código de usuario. Inténtalo para ver cómo se expande el diagrama para acomodar los otros marcos (que podrían atenuarse porque no tienes símbolos para ellos).

  2. En la ventana Pilas paralelas , asegúrese de que el botón Desplazarse automáticamente al marco de pila actual de la barra de herramientas está activado.

    Cuando tiene diagramas grandes y pasa al siguiente punto de interrupción, quizás desee que la vista se desplace automáticamente hasta el marco de pila activo del subproceso actual; es decir, el del subproceso que alcanzó primero el punto de interrupción.

  3. Antes de continuar, en la ventana Pilas paralelas , desplácese hasta la izquierda y hacia abajo.

Reanudación de la ejecución hasta el cuarto punto de interrupción

  1. Para reanudar la ejecución hasta que se alcance el cuarto punto de interrupción, en el menú Depurar , seleccione Continuar.

    Observe cómo la vista se desplazó automáticamente a su lugar. Cambie los subprocesos de la ventana Subprocesos o cambie los marcos de pila en la ventana Pila de llamadas y observe cómo la vista siempre se desplaza automáticamente al marco correcto. Desactive la opción Desplazamiento Automático al Marco de Herramienta Actual y vea la diferencia.

    La Vista de Pájaro también ayuda con diagramas grandes en la ventana Pilas Paralelas. De forma predeterminada, la vista de pájaro está activada. Puede activarlo o desactivarlo haciendo clic en el botón entre las barras de desplazamiento en la esquina inferior derecha de la ventana, como se muestra en la ilustración siguiente.

    Captura de pantalla de la vista aérea en la ventana Pilas paralelas.

    Vista panorámica en la ventana Pilas paralelas

    En la vista del pájaro, puede mover el rectángulo para desplazarse rápidamente alrededor del diagrama.

    Otra manera de mover el diagrama en cualquier dirección es seleccionar un área en blanco del diagrama y arrastrarlo donde quiera.

    Para acercar y alejar el diagrama, mantenga presionada la tecla CTRL mientras mueve la rueda del mouse. Como alternativa, seleccione el botón Zoom de la barra de herramientas y, a continuación, use la herramienta Zoom.

    También puede ver las pilas de arriba hacia abajo en lugar de abajo hacia arriba, haciendo clic en el menú Herramientas, en Opciones y, a continuación, seleccione o desactive la opción en el nodo Depuración.

  2. Antes de continuar, en el menú Depurar , seleccione Detener depuración para finalizar la ejecución.

Usar la ventana de Tareas Paralelas y la vista de Tareas de la Ventana de Pilas Paralelas

Se recomienda completar los procedimientos anteriores antes de continuar.

Reinicie la aplicación hasta que se alcance el primer punto de interrupción:

  1. En el menú Depurar , seleccione Iniciar depuración y espere a que se alcance el primer punto de interrupción.

  2. En el menú Depurar , seleccione Windows y, a continuación, seleccione Subprocesos. Acoplar la ventana Subprocesos en la parte inferior de Visual Studio.

  3. En el menú Depurar, apunte a Windows y seleccione Pila de llamadas. Acoplar la ventana Pila de llamadas en la parte inferior de Visual Studio.

  4. Haga doble clic en un subproceso en la ventana Subprocesos para establecerlo como actual. Los hilos actuales tienen la flecha amarilla. Al cambiar el hilo actual, se actualizan las demás ventanas. A continuación, examinamos las tareas.

  5. En el menú Depurar , seleccione Windows y, a continuación, seleccione Tareas. En la ilustración siguiente se muestra la ventana Tareas .

    Captura de pantalla de cuatro tareas en ejecución en la ventana Tareas.

    Cuatro tareas en ejecución en la ventana Tareas

    Para cada tarea en ejecución, puede leer su identificador, que devuelve la propiedad del mismo nombre, el identificador y el nombre del subproceso que la ejecuta, así como su ubicación (al mantener el puntero sobre ella, se muestra una información sobre herramientas con toda la pila de llamadas). Además, en la columna Tarea , puede ver el método que se pasó a la tarea; es decir, el punto de partida.

    Puede ordenar cualquier columna. Observe el glifo de ordenación que indica la columna de ordenación y la dirección. También puede reordenar las columnas arrastrándolas de izquierda o derecha.

    La flecha amarilla indica la tarea actual. Puede cambiar las tareas haciendo doble clic en una tarea o mediante el menú contextual. Al cambiar las tareas, el subproceso subyacente se vuelve actual y se actualizan las demás ventanas.

    Cuando cambia manualmente de una tarea a otra, el contorno de flecha indica el contexto actual del depurador para una tarea no actual.

    Cuando cambia manualmente de una tarea a otra, la flecha amarilla se mueve, pero una flecha blanca todavía muestra la tarea que provocó que el depurador se interrumpa.

Reanudación de la ejecución hasta el segundo punto de interrupción

Para reanudar la ejecución hasta que se alcance el segundo punto de interrupción, en el menú Depurar , seleccione Continuar.

Anteriormente, la columna Estado mostraba todas las tareas como Activas, pero ahora dos de las tareas están bloqueadas. Las tareas se pueden bloquear por muchas razones diferentes. En la columna Estado , mantenga el puntero sobre una tarea en espera para saber por qué está bloqueado. Por ejemplo, en la ilustración siguiente, la tarea 11 está esperando la tarea 12.

Captura de pantalla de dos tareas en espera en la ventana Tareas.

Anteriormente, la columna Estado mostraba todas las tareas como Activas, pero ahora dos de las tareas están bloqueadas. Las tareas se pueden bloquear por muchas razones diferentes. En la columna Estado , mantenga el puntero sobre una tarea en espera para saber por qué está bloqueado. Por ejemplo, en la siguiente ilustración, la tarea 4 está esperando a la tarea 5.

Dos tareas en espera en la ventana Tareas

La tarea 4, a su vez, está esperando en el monitor del hilo asignado a la tarea 2. (Haga clic con el botón derecho en la fila de encabezado y elija Columnas.>Asignación de subprocesos para ver el valor de asignación de subprocesos de la tarea 2).

Tarea en espera e información sobre herramientas en la ventana de Tareas

Puede marcar una tarea haciendo clic en la marca de la primera columna de la ventana Tareas .

Puede usar la marcación para realizar un seguimiento de las tareas entre distintos puntos de interrupción en la misma sesión de depuración o para filtrar las tareas cuyas pilas de llamadas se muestran en la ventana Pilas paralelas .

Cuando usó la ventana Pilas paralelas anteriormente, ha visto los subprocesos de la aplicación. Vuelva a ver la ventana Pilas paralelas , pero esta vez vea las tareas de la aplicación. Para ello, seleccione Tareas en el cuadro de la esquina superior izquierda. En la ilustración siguiente se muestra la vista Tareas.

Captura de pantalla de la vista de Tareas en la ventana Pilas paralelas.

Vista de tareas en la ventana de Pilas Paralelas

Los subprocesos que actualmente no ejecutan tareas no se muestran en la vista Tareas de la ventana Pilas paralelas . Además, para los subprocesos que ejecutan tareas, algunos de los marcos de pila que no son relevantes para las tareas se filtran desde la parte superior e inferior de la pila.

Vuelva a ver la ventana Tareas . Haga clic con el botón derecho en cualquier encabezado de columna para ver un menú contextual para la columna.

Puede usar el menú contextual para agregar o quitar columnas. Por ejemplo, la columna AppDomain no está seleccionada; por lo tanto, no se muestra en la lista. Seleccione Padre. La columna Padre aparece sin valores en cualquiera de las cuatro tareas.

Reanudación de la ejecución hasta el tercer punto de interrupción

Para reanudar la ejecución hasta que se alcance el tercer punto de interrupción, en el menú Depurar , seleccione Continuar.

Captura de pantalla de la vista Padre-hijo en la ventana Tareas.

En este ejemplo, observe que la tarea 11 y la tarea 12 se ejecutan en el mismo subproceso (muestre la columna Asignación de subprocesos si está oculta). Esta información no se muestra en la ventana Subprocesos ; verlo aquí es otra ventaja de la ventana Tareas . Para confirmarlo, vea la ventana Pilas paralelas . Asegúrese de que está viendo Tareas. Puede localizar las tareas 11 y 12 examinando la información sobre herramientas en la ventana Pilas paralelas .

Vista de tareas en la ventana Pilas paralelas

Ahora se está ejecutando una nueva tarea, tarea 5, y la tarea 4 está esperando. Para ver por qué, mantenga el puntero sobre la tarea en espera en la ventana Estado . En la columna Padre, observe que la tarea 4 es el padre de la tarea 5.

Para visualizar mejor la relación padre-hijo, haga clic con el botón derecho en la fila del encabezado de columna y luego seleccione Vista de Padre e Hijo. Debería ver la siguiente ilustración.

Vista de relaciones padre-hijo en la ventana de Tareas

Observe que la tarea 4 y la tarea 5 se ejecutan en el mismo subproceso (muestre la columna Asignación de subprocesos si está oculta). Esta información no se muestra en la ventana Subprocesos ; verlo aquí es otra ventaja de la ventana Tareas . Para confirmarlo, vea la ventana Pilas paralelas . Asegúrese de que está viendo Tareas. Busque las tareas 4 y 5 haciendo doble clic en ellas en la ventana Tareas . Cuando lo hace, se actualiza el resaltado azul en la ventana Pilas paralelas . También puede localizar las tareas 4 y 5 explorando la información sobre herramientas en la ventana Pilas paralelas.

Vista de tareas en la ventana Pilas paralelas

En la ventana Pilas paralelas, haga clic con el botón derecho en S.P y luego seleccione Ir al hilo. La ventana cambia a la vista de subprocesos y el marco correspondiente se muestra. Puedes ver ambas tareas en el mismo hilo.

Subproceso resaltado en la vista de subprocesos

Esta es otra ventaja de la vista Tareas en la ventana Pilas paralelas, en comparación con la ventana Subprocesos.

Reanudación de la ejecución hasta el cuarto punto de interrupción

Para reanudar la ejecución hasta que se alcance el tercer punto de interrupción, en el menú Depurar , seleccione Continuar. Seleccione el encabezado de columna id . para ordenar por identificador. Debería ver la siguiente ilustración.

Captura de pantalla de cuatro estados de tareas en la ventana Pilas paralelas.

La tarea 10 y la tarea 11 ahora están esperando entre sí y están bloqueadas. También hay varias tareas nuevas que ahora están programadas. Las tareas programadas son tareas que se han iniciado en el código, pero que aún no se han ejecutado. Por lo tanto, sus columnas Ubicación y asignación de subprocesos muestran mensajes predeterminados o están vacíos.

Cuatro estados de tarea en la ventana Pilas paralelas

Dado que la tarea 5 se ha completado, ya no se muestra. Si no es así en tu ordenador y el bloqueo no se muestra, avanza una vez paso a paso presionando F11.

La tarea 3 y la tarea 4 ahora están esperando entre sí y están bloqueadas. También hay 5 nuevas tareas que son elementos secundarios de la tarea 2 y ahora están programadas. Las tareas programadas son tareas que se han iniciado en el código, pero que aún no se han ejecutado. Por lo tanto, sus columnas Ubicación y asignación de subprocesos están vacías.

Vuelva a ver la ventana Pilas paralelas . El encabezado de cada cuadro tiene una información sobre herramientas que muestra los identificadores y nombres del subproceso. Cambie a la Vista de Tareas en la ventana Pilas paralelas. Mantenga el puntero sobre un encabezado para ver el identificador y el nombre de la tarea y el estado de la tarea, como se muestra en la ilustración siguiente.

Información sobre herramientas de encabezado en la ventana Pilas paralelas

Puede agrupar las tareas por columna. En la ventana Tareas , haga clic con el botón derecho en el encabezado de columna Estado y seleccione Agrupar por estado. En la ilustración siguiente se muestra la ventana Tareas agrupada por estado.

Captura de pantalla de tareas agrupadas en la ventana Tareas.

Tareas agrupadas en la ventana Tareas

Puede agrupar también por cualquier otra columna. Al agrupar tareas, puede centrarse en un subconjunto de tareas. Cada grupo contraíble tiene un recuento de los elementos agrupados.

La última característica de la ventana Tareas que se va a examinar es el menú contextual que se muestra al hacer clic con el botón derecho en una tarea.

El menú contextual muestra distintos comandos, en función del estado de la tarea. Los comandos pueden incluir Copiar, Seleccionar todo, Mostrar hexadecimal, Cambiar a tarea, Inmovilizar subproceso asignado, Inmovilizar todos los subprocesos excepto este y Descongelar subproceso asignado y Marca.

Puede congelar el subproceso subyacente de una tarea o tareas, o bien puede congelar todos los subprocesos excepto el asignado. Un subproceso inmovilizado se representa en la ventana Tareas tal y como en la ventana Subprocesos, mediante un icono de pausa azul.

Resumen

En este tutorial se muestran las ventanas del depurador Tareas paralelas y Pilas paralelas . Use estas ventanas en proyectos reales que usan código multiproceso. Puede examinar el código paralelo escrito en C++, C#o Visual Basic.