Compartir a través de


Introducción a la depuración de aplicaciones multiproceso (C#, Visual Basic, C++)

Visual Studio proporciona varias herramientas y elementos de interfaz de usuario para ayudarle a depurar aplicaciones multiproceso. En este tutorial se muestra cómo usar marcadores de subprocesos, la ventanas Pilas paralelas e Inspección paralela, y los puntos de interrupción condicionales y de filtro. Al completar este tutorial, te familiarizas con las características de Visual Studio para depurar aplicaciones multiproceso.

Estos dos artículos proporcionan información adicional sobre el uso de otras herramientas de depuración multiproceso:

El primer paso es crear un proyecto de aplicación multiproceso.

Creación de un proyecto de aplicación multiproceso

  1. Abra Visual Studio y cree un proyecto.

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

    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 plantilla Aplicación de consola para .NET 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 o introduzca MyThreadWalkthroughApp en el cuadro Nombre del proyecto. A continuación, elija Siguiente o Crear, la opción que esté disponible.

    Para un proyecto de .NET Core o .NET 5+, elija la plataforma de destino recomendada o .NET 8 y, a continuación, elija Crear.

    Aparece un nuevo proyecto de consola. Una vez creado el proyecto, aparece un archivo de origen. Según el idioma que haya elegido, es posible que se llame al archivo de origen Program.cs, MyThreadWalkthroughApp.cpp o Module1.vb.

  2. Elimine el código que aparece en el archivo de origen y reemplácelo por el código actualizado siguiente. Elija el fragmento de código adecuado para la configuración del código.

    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.");
    
        }
    }
    
  3. En el menú Archivo, seleccione Guardar todo.

  4. (Solo Visual Basic) En el Explorador de soluciones (panel derecho), haga clic con el botón derecho en el nodo del proyecto y elija Propiedades. En la pestaña Aplicación , cambie el objeto Startup a Simple.

Depuración de la aplicación multiproceso

  1. En el editor de código fuente, busque el siguiente fragmento de código:

    Thread.Sleep(3000);
    Console.WriteLine();
    
  2. Haga clic con el botón izquierdo en el margen izquierdo de la Thread.Sleep instrucción o, para C++, std::this_thread::sleep_for para insertar un nuevo punto de interrupción.

    En el margen, un círculo rojo indica que se ha establecido un punto de interrupción en esta ubicación.

  3. En el menú Depurar , seleccione Iniciar depuración (F5).

    Visual Studio compila la solución, la aplicación comienza a ejecutarse con el depurador asociado y, a continuación, la aplicación se detiene en el punto de interrupción.

  4. En el editor de código fuente, busque la línea que contiene el punto de interrupción.

Detección del marcador de subproceso

  1. En la barra de herramientas de depuración, seleccione el botón Mostrar subprocesos en código fuenteMostrar subprocesos en código fuente.

  2. Presione F11 dos veces para avanzar el depurador.

  3. Mire el canalón en el lado izquierdo de la ventana. En esta línea verá un icono de marcador de subprocesoMarcador de subproceso que se parece a dos hilos entrelazados. El marcador de subproceso indica que un subproceso se detiene en esta ubicación.

    Un punto de interrupción puede ocultar parcialmente un marcador de subproceso.

  4. Pase el puntero sobre el marcador de hilo. Aparece un DataTip que indica el nombre y el ID del hilo para cada hilo detenido. En este caso, el nombre es probablemente <noname>.

    Captura de pantalla del identificador de subproceso en un DataTip.

  5. Seleccione el marcador de subproceso para ver las opciones disponibles en el menú contextual.

Visualización de ubicaciones de subprocesos

En la ventana Pilas paralelas , puede cambiar entre una vista Subprocesos y la vista Tareas (para la programación basada en tareas) y puede ver la información de pila de llamadas para cada subproceso. En esta aplicación, podemos usar la vista de hilos.

  1. Abra la ventana Pilas paralelas seleccionando Depurar>pilas paralelas de>. Debería ver algo parecido a lo siguiente. La información exacta puede diferir en función de la ubicación actual de cada subproceso, el hardware y el lenguaje de programación.

    Captura de pantalla de la ventana de pilas paralelas.

    En este ejemplo, de izquierda a derecha vemos esta información para el código administrado:

    • El subproceso actual (flecha amarilla) ha entrado en ServerClass.InstanceMethod. Para ver el identificador de subproceso y el marco de pila de un subproceso, mantenga el puntero sobre ServerClass.InstanceMethod.
    • El subproceso 31724 está esperando un bloqueo propiedad del subproceso 20272.
    • El subproceso principal (lado izquierdo) se ha detenido en [Código externo], que puede ver con detalle si elige Mostrar código externo.

    Ventana Pilas paralelas

    En este ejemplo, de izquierda a derecha vemos esta información para el código administrado:

    • El subproceso principal (lado izquierdo) se ha detenido en Thread.Start, donde el punto de detención se identifica mediante el icono de marcador de subproceso Thread Marker.
    • Dos subprocesos han entrado en ServerClass.InstanceMethod, uno de los cuales es el subproceso actual (flecha amarilla), mientras que el otro subproceso se ha detenido en Thread.Sleep.
    • También se inicia un nuevo subproceso (a la derecha), pero se detiene en ThreadHelper.ThreadStart.
  2. Para ver los subprocesos en una vista de lista, seleccione Depurar>Windows>Subprocesos.

    Captura de pantalla de la ventana de subprocesos.

    En esta vista, puede ver fácilmente que el subproceso 20272 es el subproceso principal y actualmente se encuentra en código externo, específicamente System.Console.dll.

    Nota:

    Para obtener más información sobre el uso de la ventana Subprocesos , vea Tutorial: Depuración de una aplicación multiproceso.

  3. Haga clic con el botón derecho en las entradas de la ventana Pilas paralelas o Subprocesos para ver las opciones disponibles en el menú contextual.

    Puede realizar varias acciones en estos menús con el botón derecho. En este tutorial, explorará con más profundidad los detalles de la ventana Inspección paralela en las siguientes secciones.

Establecimiento de una inspección en una variable

  1. Abra la ventana Inspección Paralela seleccionando Depurar>Windows>Inspección Paralela>Inspección Paralela 1.

  2. Seleccione la celda donde verá el <Add Watch> texto (o la celda de encabezado vacía en la cuarta columna) y escriba data.

    Los valores de la variable de datos para cada subproceso aparecen en la ventana.

  3. Seleccione la celda donde verá el <Add Watch> texto (o la celda de encabezado vacía en la quinta columna) y escriba count.

    Los valores de la count variable para cada subproceso aparecen en la ventana. Si todavía no ve mucha información, intente presionar F11 varias veces para avanzar la ejecución de los subprocesos en el depurador.

    Ventana de inspección paralela

  4. Haga clic con el botón derecho en una de las filas de la ventana para ver las opciones disponibles.

Marcar y desmarcar subprocesos

Puede marcar subprocesos para realizar un seguimiento de los subprocesos importantes e ignorar los demás subprocesos.

  1. En la ventana Inspección paralela , mantenga presionada la tecla Mayús y seleccione varias filas.

  2. Haga clic con el botón derecho y seleccione Marca.

    Se marcarán todos los subprocesos seleccionados. Ahora, puedes filtrar para mostrar solo los hilos marcados.

  3. En la ventana Inspección Paralela, seleccione el botón Mostrar solo subprocesos marcadosMostrar subprocesos marcados.

    Solo los hilos marcados aparecen en la lista.

    Sugerencia

    Después de marcar algunos subprocesos, puede hacer clic con el botón derecho en una línea de código en el editor de código y elegir Ejecutar subprocesos marcados en cursor. Asegúrese de elegir código al que accedan todos los subprocesos marcados. Visual Studio pausará los subprocesos en la línea de código seleccionada, lo que facilita el control del orden de ejecución mediante la congelación y el descongelamiento de subprocesos.

  4. Seleccione el botón Mostrar solo subprocesos marcados de nuevo para volver a activar el modo Mostrar todos los subprocesos .

  5. Para quitar los marcadores de subprocesos, haga clic con el botón derecho en uno o más subprocesos marcados en la ventana Inspección paralela y seleccione Quitar marca.

Inmovilización y reanudación de la ejecución de subprocesos

Sugerencia

Puede congelar y descongelar (suspender y reanudar) subprocesos para controlar el orden en el que los subprocesos realizan su trabajo. Esto puede ayudarle a resolver incidencias de simultaneidad como interbloqueos y condiciones de carrera.

  1. En la ventana Inspección paralela, con todas las filas seleccionadas, haga clic con el botón derecho y seleccione Inmovilizar.

    En la segunda columna, aparece un icono de pausa para cada fila. El icono de pausa indica que el subproceso está inmovilizado.

  2. Anule la selección de todas las demás filas seleccionando solo una fila.

  3. Haga clic con el botón derecho en una fila y seleccione Reanudar.

    El icono de pausa desaparece de esta fila, lo que indica que el subproceso ya no está inmovilizado.

  4. Cambie al editor de código y presione F11. Solo se ejecuta el subproceso inmovilizado.

    La aplicación también puede crear instancias de algunos subprocesos nuevos. Los subprocesos nuevos no están marcados ni se inmovilizan.

Seguimiento de un solo subproceso con puntos de interrupción condicionales

Puede ser útil seguir la ejecución de un único subproceso en el depurador. Una forma de hacerlo consiste en inmovilizar los subprocesos que no le interesan. En algunos escenarios, es posible que tenga que seguir un único subproceso sin inmovilizar otros subprocesos, por ejemplo, para reproducir un error determinado. Para seguir un subproceso sin inmovilizar los demás, debe evitar interrumpir el código, excepto en el subproceso que le interese. Puede realizar esta tarea estableciendo un punto de interrupción condicional.

Puede establecer puntos de interrupción en condiciones diferentes, como el nombre del subproceso o el identificador de subproceso. Puede resultar útil establecer la condición en los datos que sabe que son únicos para cada subproceso. Este enfoque es común durante la depuración si le interesa más algún valor de datos concreto que cualquier subproceso concreto.

  1. Haga clic con el botón derecho en el punto de interrupción que creó anteriormente y seleccione Condiciones.

  2. En la ventana Configuración del punto de interrupción , escriba data == 5 para la expresión condicional.

    Punto de interrupción condicional

    Sugerencia

    Si estás más interesado en un subproceso específico, usa un nombre de subproceso o un identificador de subproceso como condición. Para ello, en la ventana Configuración del punto de interrupción , seleccione Filtrar en lugar de Expresión condicional y siga las sugerencias de filtro. Es posible que quiera asignar un nombre a los subprocesos en el código de la aplicación, ya que los identificadores de subprocesos cambian al reiniciar el depurador.

  3. Cierre la ventana Configuración del punto de interrupción .

  4. Seleccione el botón de Reiniciar Reiniciar aplicación para volver a iniciar la sesión de depuración.

    Se interrumpe el código en el subproceso en el que el valor de la variable de datos es 5. En la ventana Inspección paralela, busque la flecha de color amarillo que indica el contexto del depurador actual.

  5. Ahora, puede depurar el código paso a paso por procedimientos (F10) y paso a paso por instrucciones (F11), y seguir la ejecución del subproceso único.

    Siempre y cuando la condición de punto de interrupción sea única para el subproceso, y el depurador no alcance ningún otro punto de interrupción en otros subprocesos (es posible que tenga que deshabilitarlos), puede depurar el código paso a paso por procedimientos y por instrucciones sin cambiar a otros subprocesos.

    Nota:

    Al avanzar el depurador, se ejecutarán todos los subprocesos. Pero el depurador no interrumpirá el código en otros subprocesos a menos que uno llegue a un punto de interrupción.