Procedura dettagliata: debug di un'applicazione parallela in Visual Studio (C#, Visual Basic, C++)

In questa procedura dettagliata viene illustrato come usare le finestre Attività in parallelo e Stack in parallelo per eseguire il debug di un'applicazione parallela. Queste finestre consentono di comprendere e verificare il comportamento di runtime del codice che usa la libreria TPL (Task Parallel Library) o il runtime di concorrenza. Nella procedura dettagliata viene fornito un esempio di codice con punti di interruzione incorporati. Una volta interrotta l'esecuzione del codice, viene illustrato come esaminarlo usando le finestre Attività in parallelo e Stack in parallelo.

Nella procedura dettagliata vengono spiegate le seguenti attività:

  • Come visualizzare gli stack di chiamate di tutti i thread in un'unica visualizzazione.

  • Come visualizzare l'elenco delle istanze di System.Threading.Tasks.Task create nell'applicazione.

  • Come visualizzare gli stack di chiamate reali delle attività anziché dei thread.

  • Come passare al codice dalle finestre Attività in parallelo e Stack in parallelo.

  • Come le finestre affrontano il ridimensionamento tramite il raggruppamento, lo zoom e altre funzionalità correlate.

Prerequisiti

Questa procedura dettagliata presuppone che Just My Code sia abilitato (è abilitato per impostazione predefinita nelle versioni più recenti di Visual Studio). Nel menu Strumenti selezionare Opzioni, espandere il nodo Debug, selezionare Generale e quindi selezionare Abilita Just My Code (solo gestito) . Se non si imposta questa funzionalità, è comunque possibile usare questa procedura dettagliata, ma i risultati potrebbero differire dalle illustrazioni.

Esempio in C#

Se si utilizza l'esempio in C#, la procedura dettagliata presuppone inoltre che il codice esterno sia nascosto. Per attivare o disattivare la visualizzazione del codice esterno, fare clic con il pulsante destro del mouse sull'intestazione della tabella Nome nella finestra Stack di chiamate, quindi selezionare o deselezionare Mostra codice esterno. Se non si imposta questa funzionalità, è comunque possibile usare questa procedura dettagliata, ma i risultati potrebbero differire dalle illustrazioni.

Esempio di C++

Se si usa l'esempio C++, è possibile ignorare i riferimenti al codice esterno in questo articolo. Il codice esterno si applica solo all'esempio in C#.

Illustrazioni

Le illustrazioni in questo articolo vengono registrate in un computer quad core che esegue l'esempio C#. Sebbene sia possibile usare altre configurazioni per completare questa procedura dettagliata, le illustrazioni potrebbero differire da quelle visualizzate nel computer.

Creare il progetto di esempio

L'esempio di codice riportato in questa procedura dettagliata è relativo a un'applicazione che non esegue alcuna operazione. Lo scopo dell'esercizio è comprendere come usare le finestre degli strumenti per eseguire il debug di un'applicazione parallela.

  1. Aprire Visual Studio e creare un nuovo progetto.

    Se la finestra iniziale non è aperta, scegliere Finestra di avvio file>.

    Nella finestra iniziale scegliere Nuovo progetto.

    Nella finestra iniziale scegliere Crea un nuovo progetto.

    Nella finestra Crea un nuovo progetto immettere o digitare console nella casella di ricerca. Scegliere quindi C#, C++o Visual Basic dall'elenco Linguaggio e quindi scegliere Windows dall'elenco Piattaforma.

    Dopo aver applicato i filtri del linguaggio e della piattaforma, scegliere l'app console per .NET Core o C++, quindi scegliere Avanti.

    Nota

    Se il modello corretto non è visualizzato, passare a Strumenti Ottieni strumenti>e funzionalità, che apre il Programma di installazione di Visual Studio. Scegliere il carico di lavoro Sviluppo per desktop .NET o Sviluppo di applicazioni desktop con C++, quindi scegliere Modifica.

    Nella finestra Configura il nuovo progetto digitare un nome o usare il nome predefinito nella casella Nome progetto. Scegliere quindi Avanti o Crea, a qualsiasi opzione disponibile.

    Per .NET Core scegliere il framework di destinazione consigliato o .NET 8 e quindi scegliere Crea.

    Verrà visualizzato un nuovo progetto console. Dopo aver creato il progetto, viene visualizzato un file di origine.

  2. Aprire il file di codice con estensione CPP, CS o VB nel progetto. Eliminarne il contenuto per creare un file di codice vuoto.

  3. Incollare il seguente codice per il linguaggio selezionato nel file di codice vuoto.

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

Dopo aver aggiornato il file di codice, salvare le modifiche e compilare la soluzione.

  1. Scegliere Save All (Salva tutto) dal menu File.

  2. Scegliere Ricompila soluzione dal menu Compila.

Si noti che sono presenti quattro chiamate a Debugger.Break (DebugBreak nell'esempio C++). Pertanto, non è necessario inserire punti di interruzione. L'esecuzione dell'applicazione comporta l'interruzione del debugger fino a quattro volte.

Usare la finestra Stack paralleli: visualizzazione Thread

Per iniziare, scegliere Avvia debug dal menu Debug. Attendere che venga raggiunto il primo punto di interruzione.

Visualizzare lo stack di chiamate di un singolo thread

  1. Scegliere Windows dal menu Debug e quindi thread. Ancorare la finestra Thread nella parte inferiore di Visual Studio.

  2. Scegliere Windows dal menu Debug e quindi selezionare Stack di chiamate. Ancorare la finestra Stack di chiamate nella parte inferiore di Visual Studio.

  3. Fare doppio clic su un thread nella finestra Thread per far sì che diventi il thread corrente. I thread correnti presentano una freccia gialla. Quando si modifica il thread corrente, il relativo stack di chiamate viene visualizzato nella finestra Stack di chiamate.

Esaminare la finestra Stack paralleli

Scegliere Windows dal menu Debug e quindi stack paralleli. Assicurarsi che Thread sia selezionata nella casella nell'angolo superiore sinistro.

Usando la finestra Stack paralleli , è possibile visualizzare più stack di chiamate contemporaneamente in un'unica visualizzazione. La figura seguente mostra la finestra Stack paralleli sopra la finestra Stack di chiamate.

Screenshot of Threads view in Parallel Stacks window.

Threads view in Parallel Stacks window

Lo stack di chiamate del thread principale viene visualizzato in una casella, mentre gli stack di chiamate degli altri quattro thread sono raggruppati in un'altra casella. I quattro thread vengono raggruppati insieme in quanto i rispettivi stack frame condividono gli stessi contesti del metodo, vale a dire si trovano negli stessi metodi: A, B e C. Per visualizzare gli ID thread e i nomi dei thread che condividono la stessa casella, passare il puntatore del mouse sulla casella con l'intestazione ([#] Thread). Il thread corrente viene visualizzato in grassetto.

Screenshot of Tooltip that shows thread IDs and names.

Tooltip that shows thread IDs and names

La freccia gialla indica lo stack frame attivo del thread corrente.

È possibile impostare i dettagli da visualizzare per gli stack frame (Nomi moduli, Tipi di parametro, Nomi parametri, Valori di parametro, Numeri di riga e Offset dei byte) facendo clic con il pulsante destro del mouse nella finestra Stack di chiamate.

Un'evidenziazione blu attorno a una casella indica che il thread corrente è parte di quella casella. Il thread corrente è inoltre indicato dallo stack frame in grassetto nella descrizione comandi. Se si fa doppio clic sul thread principale nella finestra Thread, è possibile osservare che la freccia di evidenziazione nella finestra Stack paralleli si sposta di conseguenza.

Screenshot of Highlighted main thread in Parallel Stacks window.

Highlighted main thread in Parallel Stacks window

Riprendere l'esecuzione fino al secondo punto di interruzione

Per riprendere l'esecuzione fino a quando non viene raggiunto il secondo punto di interruzione, scegliere Continua dal menu Debug. Nell'illustrazione seguente viene mostrata la struttura ad albero del thread in corrispondenza del secondo punto di interruzione.

Screenshot of Parallel Stacks window that shows many branches.

Parallel Stacks window that shows many branches

In corrispondenza del primo punto di interruzione, i quattro thread erano passati tutti dal metodo S.A a S.B e a S.C. Le informazioni sono ancora visibili nella finestra Stack in parallelo, ma i quattro thread sono avanzati. Uno di loro continuò a S.D e poi S.E. Un altro continuò a S.F, S.G e S.H. Altri due continuarono a S.I e S.J, e da lì uno di loro andò a S.K e l'altro continuò a nonuser External Code.

Passando invece il mouse sugli stack frame, verranno visualizzati gli ID dei thread e altri dettagli sui frame. L'evidenziazione blu indica il thread corrente, mentre la freccia gialla indica lo stack frame attivo del thread corrente.

È possibile passare il mouse sull'intestazione della casella, ad esempio 1 thread o 2 thread, per visualizzare gli ID dei thread. Passando invece il mouse sugli stack frame, verranno visualizzati gli ID dei thread e altri dettagli sui frame. L'evidenziazione blu indica il thread corrente, mentre la freccia gialla indica lo stack frame attivo del thread corrente.

L'icona dei thread di stoffa (linee interweaved) indica gli stack frame attivi dei thread non simultanei. Fare doppio clic su S.B nella finestra Stack di chiamate per passare da un frame all'altro. La finestra Stack paralleli indica lo stack frame corrente del thread corrente usando un'icona a forma di freccia curva.

Nota

Per una descrizione di tutte le icone nella finestra Stack paralleli, vedere Uso della finestra Stack paralleli.

Passare da un thread all'altro nella finestra Thread e osservare che la visualizzazione nella finestra Stack in parallelo viene aggiornata.

Per passare a un altro thread, o a un altro frame di un altro thread, è possibile usare il menu di scelta rapida nella finestra Stack in parallelo. Ad esempio, fare clic con il pulsante destro del mouse su S.J, scegliere Passa a frame e quindi selezionare un comando.

Screenshot of Parallel Stacks Path of Execution.

Parallel Stacks Path of Execution

Fare clic con il pulsante destro del mouse su S.C e scegliere Passa a frame. Uno dei comandi presenta un segno di spunta che indica lo stack frame del thread corrente. È possibile passare a quel fotogramma dello stesso thread (solo le mosse della freccia curva) oppure passare all'altro thread (anche l'evidenziazione blu si sposta). Nell'illustrazione seguente viene mostrato il sottomenu.

Screenshot of Stacks menu with 2 options on C while J is current.

Stacks menu with 2 options on C while J is current

Quando un contesto del metodo è associato a un unico stack frame, nell'intestazione della casella viene visualizzato 1 thread; fare doppio clic per passare a esso. Facendo doppio clic su un contesto del metodo al quale sono associati più frame, verrà visualizzato automaticamente il menu. Passando il mouse sui contesti del metodo, notare il triangolo nero sulla destra. Il menu di scelta rapida verrà visualizzato anche facendo clic su quel triangolo.

Per le applicazioni di grandi dimensioni con molti thread, è possibile concentrarsi solo su un subset di thread. Nella finestra Stack in parallelo è possibile visualizzare gli stack di chiamate per i soli thread con contrassegno. Per contrassegnare i thread, utilizzare il menu di scelta rapida o la prima cella di un thread.

Sulla barra degli strumenti selezionare il pulsante Mostra solo contrassegnato accanto alla casella di riepilogo.

Screenshot of Parallel Stacks window and tooltip.

Parallel Stacks window and tooltip

Ora, nella finestra Stack paralleli vengono visualizzati solo i thread contrassegnati .

Riprendere l'esecuzione fino al terzo punto di interruzione

  1. Per riprendere l'esecuzione fino a quando non viene raggiunto il terzo punto di interruzione, scegliere Continua dal menu Debug.

    Quando più thread si trovano nello stesso metodo, ma il metodo non era all'inizio dello stack di chiamate, il metodo viene visualizzato in caselle diverse. Un esempio in corrispondenza del punto di interruzione corrente è S.L, il quale contiene tre thread e viene visualizzato in tre caselle. Fare doppio clic su S.L.

    Screenshot of Execution path in Parallel Stacks window.

    Execution path in Parallel Stacks window

    Si noti che S.L appare in grassetto nelle altre due caselle, così da poter vedere in quali altri punti viene visualizzato. Per visualizzare i fotogrammi che chiamano in S.L e quali fotogrammi chiamano, selezionare il pulsante Attiva/Disattiva visualizzazione metodo sulla barra degli strumenti. La figura seguente mostra la visualizzazione del metodo della finestra Stack paralleli .

    Screenshot of Method view in Parallel Stacks window.

    Method view in Parallel Stacks window

    Si noti come il diagramma sia imperniato sul metodo selezionato e lo abbia posizionato nella propria casella al centro della visualizzazione. I chiamanti e i chiamanti vengono visualizzati rispettivamente nella parte superiore e inferiore. Selezionare di nuovo il pulsante Attiva/Disattiva visualizzazione metodo per lasciare questa modalità.

    Il menu di scelta rapida della finestra Stack in parallelo presenta inoltre le voci descritte di seguito.

    • Visualizzazione esadecimale consente di scegliere tra il formato decimale e il formato esadecimale dei numeri nelle descrizioni comandi.

    • Simbolo Impostazioni aprire le rispettive finestre di dialogo.

    • Mostra thread in Origine attiva/disattiva la visualizzazione dei marcatori di thread nel codice sorgente, che mostra la posizione dei thread nel codice sorgente.

    • Mostra codice esterno visualizza tutti i fotogrammi anche se non sono nel codice utente. Provare a visualizzare il diagramma che si espande per contenere gli altri fotogrammi (che potrebbero essere disattivati perché non sono presenti simboli per loro).

  2. Nella finestra Stack in parallelo assicurarsi che il pulsante Scorrimento automatico a stack frame corrente nella barra degli strumenti sia attivato.

    Quando si dispone di diagrammi di grandi dimensioni e si passa al punto di interruzione successivo, è possibile che la visualizzazione scori automaticamente fino al frame dello stack attivo del thread corrente; ovvero il thread che ha raggiunto prima il punto di interruzione.

  3. Prima di continuare, nella finestra Stack in parallelo scorrere fino in fondo verso sinistra e verso il basso.

Riprendere l'esecuzione fino al quarto punto di interruzione

  1. Per riprendere l'esecuzione fino a quando non viene raggiunto il quarto punto di interruzione, scegliere Continua dal menu Debug.

    Si noti come la visualizzazione sia scorsa automaticamente fino al punto in questione. Passare da un thread all'altro nella finestra Thread o da uno stack frame all'altro nella finestra Stack di chiamate e notare come la visualizzazione scorra sempre automaticamente al frame corretto. Disattivare l'opzione Scorrimento automatico a stack frame corrente e osservare la differenza.

    La Visualizzazione panoramica risulta utile anche con diagrammi di grandi dimensioni nella finestra Stack in parallelo. Per impostazione predefinita, la visualizzazione occhio dell'uccello è attiva. È tuttavia possibile attivarlo facendo clic sul pulsante tra le barre di scorrimento nell'angolo inferiore destro della finestra, come illustrato nella figura seguente.

    Screenshot of Birds eye view in Parallel Stacks window.

    Bird's-eye view in Parallel Stacks window

    Nella visualizzazione degli occhi degli uccelli, è possibile spostare il rettangolo per eseguire rapidamente una panoramica intorno al diagramma.

    Un altro modo per spostare il diagramma in qualsiasi direzione consiste nel selezionare un'area vuota del diagramma e trascinarla nel punto desiderato.

    Per lo zoom avanti e indietro del diagramma, tenere premuto CTRL mentre si muove la rotella del mouse. In alternativa, selezionare il pulsante Zoom sulla barra degli strumenti e quindi usare lo strumento Zoom.

    È anche possibile visualizzare gli stack dall'alto verso il basso, anziché dal basso verso l'alto. A tale scopo, fare clic sul menu Strumenti, scegliere Opzioni, quindi selezionare o deselezionare l'opzione nel nodo Debug.

  2. Prima di continuare, scegliere Arresta debug dal menu Debug per terminare l'esecuzione.

Usare la finestra Attività parallele e la visualizzazione Attività della finestra Stack paralleli

Prima di proseguire, si consiglia di completare le procedure precedenti.

Riavviare l'applicazione fino a quando non viene raggiunto il primo punto di interruzione:

  1. Nel menu Debug selezionare Avvia debug e attendere che venga raggiunto il primo punto di interruzione.

  2. Scegliere Windows dal menu Debug e quindi thread. Ancorare la finestra Thread nella parte inferiore di Visual Studio.

  3. Scegliere Windows dal menu Debug e selezionare Stack di chiamate. Ancorare la finestra Stack di chiamate nella parte inferiore di Visual Studio.

  4. Fare doppio clic su un thread nella finestra Thread per far sì che diventi il thread corrente. I thread correnti presentano la freccia gialla. Quando si modifica il thread corrente, le altre finestre vengono aggiornate. Verranno quindi esaminate le attività.

  5. Scegliere Windows dal menu Debug e quindi selezionare Attività. La figura seguente mostra la finestra Attività .

    Screenshot of Four running tasks in Tasks window.

    Four running tasks in Tasks window

    Per ogni attività in esecuzione è possibile visualizzare il relativo ID, restituito dalla proprietà omonima, l'ID e il nome del thread che la esegue e il relativo percorso (passando il mouse sul percorso viene visualizzata una descrizione comandi con l'intero stack di chiamate). Inoltre, nella colonna Attività è possibile visualizzare il metodo passato nell'attività, in altre parole il punto iniziale.

    Le colonne possono essere ordinate. Si noti il glifo di ordinamento che indica la colonna e la direzione di ordinamento. È anche possibile riordinare le colonne trascinandole a sinistra o a destra.

    La freccia gialla indica l'attività corrente. Per passare da un'attività all'altra, fare doppio clic su un'attività o utilizzare il menu di scelta rapida. Quando si passa da un'attività all'altra, il thread sottostante diventa quello corrente e le altre finestre vengono aggiornate.

    Quando si passa manualmente da un'attività a un'altra, la struttura freccia indica il contesto del debugger corrente per un'attività non simultanea.

    Quando si passa manualmente da un'attività a un'altra, la freccia gialla si sposta, ma una freccia bianca continua a mostrare l'attività che ha causato l'interruzione nel debugger.

Riprendere l'esecuzione fino al secondo punto di interruzione

Per riprendere l'esecuzione fino a quando non viene raggiunto il secondo punto di interruzione, scegliere Continua dal menu Debug.

In precedenza, la colonna Stato mostrava tutte le attività come Attivo, ma ora due delle attività sono Bloccate. Le attività possono essere bloccate per molti motivi diversi. Nella colonna Stato passare il puntatore del mouse su un'attività in attesa per scoprire perché è bloccato. Nell'illustrazione seguente, ad esempio, l'attività 11 è in attesa dell'attività 12.

Screenshot of Two waiting tasks in Tasks window.

In precedenza, la colonna Stato mostrava tutte le attività come Attivo, ma ora due delle attività sono Bloccate. Le attività possono essere bloccate per molti motivi diversi. Nella colonna Stato passare il puntatore del mouse su un'attività in attesa per scoprire perché è bloccato. Nell'illustrazione seguente, ad esempio, l'attività 4 è in attesa dell'attività 5.

Two waiting tasks in Tasks window

L'attività 4, a sua volta, è in attesa di un monitoraggio di proprietà del thread assegnato all'attività 2. Fare clic con il pulsante destro del mouse sulla riga di intestazione e scegliere Assegnazione thread colonne>per visualizzare il valore di assegnazione del thread per l'attività 2.

Waiting task and tooltip in Tasks window

È possibile contrassegnare un'attività facendo clic sul flag nella prima colonna della finestra Attività .

È possibile usare i contrassegni per tenere traccia delle attività tra i vari punti di interruzione nella stessa sessione di debug o per filtrare le attività i cui stack di chiamate vengono visualizzati nella finestra Stack in parallelo.

Quando in precedenza è stata usata la finestra Stack in parallelo, sono stati visualizzati i thread dell'applicazione. Visualizzando nuovamente la finestra Stack in parallelo, questa volta si vedranno le attività dell'applicazione. A tale scopo, selezionare Attività nella casella in alto a sinistra. Nell'illustrazione che segue viene mostrata la visualizzazione Attività.

Screenshot of Tasks view in Parallel Stacks window.

Tasks view in Parallel Stacks window

I thread che attualmente non eseguono attività non vengono visualizzati nella visualizzazione Attività della finestra Stack paralleli . Inoltre, per i thread che eseguono attività, alcuni stack frame che non sono rilevanti per le attività vengono filtrati dalla parte superiore e inferiore dello stack.

Visualizzare di nuovo la finestra Attività . Fare clic con il pulsante destro del mouse su un'intestazione di colonna qualsiasi per visualizzare un menu di scelta rapida per la colonna.

Il menu di scelta rapida può essere utilizzato per aggiungere o rimuovere colonne. Ad esempio, la colonna AppDomain non è selezionata; pertanto, non viene visualizzato nell'elenco. Selezionare Padre. Verrà visualizzata la colonna Padre senza valori per le quattro attività.

Riprendere l'esecuzione fino al terzo punto di interruzione

Per riprendere l'esecuzione fino a quando non viene raggiunto il terzo punto di interruzione, scegliere Continua dal menu Debug.

Screenshot of Parent-child view in Tasks window.

In questo esempio si noti che l'attività 11 e l'attività 12 sono in esecuzione nello stesso thread (mostra la colonna Assegnazione thread se è nascosta). Queste informazioni non vengono visualizzate nella finestra Thread . Di seguito è riportato un altro vantaggio della finestra Attività . Per conferma, visualizzare la finestra Stack in parallelo. Assicurarsi di visualizzare Attività. È possibile individuare le attività 11 e 12 analizzando le descrizioni comando nella finestra Stack paralleli .

Task view in Parallel Stacks window

Una nuova attività, l'attività 5, è ora in esecuzione mentre l'attività 4 è in attesa. Per visualizzare il motivo, passare il mouse sull'attività in attesa nella finestra Stato. Nella colonna Padre si noti che l'attività 4 è l'elemento padre dell'attività 5.

Per visualizzare meglio la relazione padre-figlio, fare clic con il pulsante destro del mouse sulla riga dell'intestazione di colonna e quindi scegliere Visualizzazione padre figlio. Si dovrebbe vedere l'illustrazione seguente.

Parent-child view in Tasks window

Si noti che l'attività 4 e l'attività 5 sono in esecuzione nello stesso thread (visualizzare la colonna Assegnazione thread se è nascosta). Queste informazioni non vengono visualizzate nella finestra Thread . Di seguito è riportato un altro vantaggio della finestra Attività . Per conferma, visualizzare la finestra Stack in parallelo. Assicurarsi di visualizzare Attività. Individuare le attività 4 e 5 facendo doppio clic su di esse nella finestra Attività . L'evidenziazione blu nella finestra Stack in parallelo verrà di conseguenza aggiornata. È anche possibile individuare le attività 4 e 5 analizzando le descrizioni comandi nella finestra Stack in parallelo.

Task view in Parallel Stacks window

Nella finestra Stack paralleli fare clic con il pulsante destro del mouse su S.P e quindi scegliere Vai al thread. La finestra passerà alla visualizzazione Thread e verrà visualizzato il thread corrispondente. È possibile vedere entrambe le attività nello stesso thread.

Highlighted thread in threads view

Questo è un altro vantaggio della visualizzazione Attività nella finestra Stack in parallelo rispetto alla finestra Thread.

Riprendere l'esecuzione fino al quarto punto di interruzione

Per riprendere l'esecuzione fino a quando non viene raggiunto il terzo punto di interruzione, scegliere Continua dal menu Debug. Selezionare l'intestazione di colonna ID per ordinare in base all'ID. Si dovrebbe vedere l'illustrazione seguente.

Screenshot of Four task states in Parallel Stacks window.

L'attività 10 e l'attività 11 sono ora in attesa l'una sull'altra e sono bloccate. Sono inoltre disponibili diverse nuove attività che sono ora pianificate. Le attività pianificate sono attività avviate nel codice ma non ancora eseguite. Di conseguenza, le colonne Location e Thread Assignment mostrano i messaggi predefiniti o sono vuoti.

Four task states in Parallel Stacks window

Poiché l'attività 5 è stata completata, non viene più visualizzata. Se non è così nel computer e il deadlock non viene visualizzato, premere F11 una volta.

L'attività 3 e l'attività 4 sono ora in attesa l'una sull'altra e sono bloccate. Vi sono inoltre 5 nuove attività che sono attività figlio dell'attività 2 e vengono ora pianificate. Le attività pianificate sono attività avviate nel codice ma non ancora eseguite. Le relative colonne Percorso e Assegnazione thread sono pertanto vuote.

Visualizzare nuovamente la finestra Stack in parallelo. L'intestazione di ogni casella presenta una descrizione comandi nella quale sono visualizzati gli ID e i nomi dei thread. Passare alla visualizzazione Attività nella finestra Stack in parallelo. Passare il mouse su un'intestazione per visualizzare l'ID, il nome e lo stato dell'attività come mostrato nell'illustrazione seguente.

Header tooltip in Parallel Stacks window

È possibile raggruppare le attività in base alle colonne. Nella finestra Attività fare clic con il pulsante destro del mouse sull'intestazione di colonna Stato e quindi scegliere Raggruppa per stato. La figura seguente mostra la finestra Attività raggruppata per stato.

Screenshot of Grouped tasks in Tasks window.

Grouped tasks in Tasks window

È anche possibile raggruppare in base alle altre colonne. Raggruppando le attività, è possibile concentrarsi su un subset di attività. Ogni gruppo comprimibile presenta un conteggio degli elementi raggruppati insieme.

L'ultima funzionalità della finestra Attività da esaminare è il menu di scelta rapida visualizzato quando si fa clic con il pulsante destro del mouse su un'attività.

Il menu di scelta rapida visualizza comandi diversi a seconda dello stato dell'attività. I comandi possono includere Copia, Seleziona tutto, Visualizzazione esadecimale, Passa all'attività, Blocca thread assegnato, Blocca tutti i thread, ma questo e Thaw Assigned Thread e Flag.

È possibile bloccare il thread sottostante di un'attività o di più attività oppure bloccare tutti i thread a eccezione di quello assegnato. Un thread bloccato viene rappresentato nella finestra Attività, così come si trova nella finestra Thread, da un'icona di pausa blu.

Riepilogo

In questa procedura dettagliata sono state illustrate le finestre del debugger Attività in parallelo e Stack in parallelo. Utilizzare queste finestre con progetti reali che a loro volta utilizzano codice multithreading. È possibile esaminare codice parallelo scritto in C++, C# o Visual Basic.