Condividi tramite


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

Questa procedura dettagliata illustra come usare le finestre Attività parallele e Stack paralleli 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. Questa procedura dettagliata fornisce codice di esempio con punti di interruzione predefiniti. Dopo l'interruzione del codice, la guida dettagliata illustra come usare le finestre Attività parallele e Stack paralleli per esaminarlo.

Questa procedura dettagliata illustra queste attività:

  • Come visualizzare gli stack di chiamate di tutti i thread in una sola visualizzazione.

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

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

  • Come passare al codice dalle finestre Attività parallele e Stack paralleli .

  • Modalità di ridimensionamento delle finestre tramite raggruppamento, 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 usa l'esempio C#, questa procedura dettagliata presuppone anche che il codice esterno sia nascosto. Per attivare o disattivare la visualizzazione di codice esterno, fare clic con il pulsante destro del mouse sull'intestazione della tabella Nome della finestra Stack di chiamate e 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 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

Il codice di esempio in questa procedura dettagliata riguarda 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 Finestradi avvio>.

    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.

    Annotazioni

    Se non vedi il modello corretto, vai a Strumenti>Recupera strumenti e funzionalità... Verrà aperto il programma di installazione di Visual Studio. Scegliere il carico di lavoro Sviluppo di desktop .NET o Sviluppo di applicazioni desktop con C++ e quindi scegliere Modifica.

    Nella finestra Configura il nuovo progetto digitare un nome o usare il nome predefinito nella casella Nome progetto . Quindi, scegliere Avanti o Crea, qualunque opzione sia disponibile.

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

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

  2. Aprire il file di codice .cpp, .cs o .vb nel progetto. Eliminare il contenuto per creare un file di codice vuoto.

  3. Incollare il codice seguente per la lingua scelta 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 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 dei 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. Sul menu Debug, posizionare il puntatore su Windows e quindi selezionare Thread. Ancorare la finestra Threads 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 renderlo corrente. I thread correnti hanno una freccia gialla. Quando si modifica il thread corrente, lo stack di chiamate viene visualizzato nella finestra Stack di chiamate .

Esaminare la finestra Stack paralleli

Nel menu Debug, puntare a Windows e quindi selezionare Parallel Stacks. Accertarsi che Threads sia selezionato nella casella nell'angolo superiore sinistro.

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

Screenshot della visualizzazione Thread nella finestra Stack paralleli.

Visualizzazione dei thread nella finestra Stack Paralleli

Lo stack di chiamate del thread principale viene visualizzato in una casella e gli stack di chiamate per gli altri quattro thread vengono raggruppati in un'altra casella. Quattro thread vengono raggruppati perché i frame dello stack condividono gli stessi contesti di metodo; ovvero si trovano negli stessi metodi: A, Be 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 del tooltip che mostra gli ID e i nomi dei thread.

Descrizione comando che mostra gli ID e i nomi dei thread

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

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

Un'evidenziazione blu intorno a una casella indica che il thread corrente fa parte di quella casella. Il thread corrente è indicato anche dal frame dello stack in grassetto nel tooltip. 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 del thread principale evidenziato nella finestra degli Stack paralleli.

Thread principale evidenziato nella finestra Stack paralleli

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. L'illustrazione seguente mostra l'albero del thread nel secondo punto di interruzione.

Schermata della finestra Stack paralleli che mostra molti rami.

Finestra Stacchi paralleli che mostra molte diramazioni

Al primo punto di interruzione, quattro thread passavano da S.A a S.B ai metodi S.C. Le informazioni sono ancora visibili nella finestra Parallel Stacks, ma i quattro thread sono ulteriormente progrediti. 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.

È possibile passare il puntatore del mouse sugli stack frame per visualizzare gli ID thread e altri dettagli del frame. L'evidenziazione blu indica il thread corrente e la freccia gialla indica lo stack frame attivo del thread corrente.

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

L'icona dei thread (linee intrecciate) indica gli stack frame attivi dei thread non correnti. Nella finestra Stack di chiamate, fare doppio clic su S.B per cambiare contesto. La finestra Stack paralleli visualizza il frame dello stack attuale del thread correntemente utilizzato, utilizzando un'icona a forma di freccia curva.

Annotazioni

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

Nella finestra Thread passare da un thread all'altro e osservare che la visualizzazione nella finestra Stack paralleli viene aggiornata.

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

Screenshot del percorso di esecuzione di stack paralleli.

Percorso di esecuzione di stack paralleli

Fare clic con il pulsante destro del mouse su S.C e scegliere Passa a frame. Uno dei comandi ha un segno di spunta che indica lo stack frame del thread corrente. Puoi passare a quel fotogramma dello stesso thread (si muove solo la freccia curva) oppure puoi passare all'altro thread (anche l'evidenziazione blu si sposta). La figura seguente mostra il sottomenu.

Screenshot del menu Stack con 2 opzioni su C mentre J è attivo.

Menu Stacks con 2 opzioni in C mentre J è selezionato

Quando un contesto di metodo è associato a un solo stack frame, l'intestazione della casella visualizza 1 Thread ed è possibile passarvi facendo doppio clic. Se si fa doppio clic su un contesto di metodo con più di 1 frame associato, viene visualizzato automaticamente il menu. Quando si passa il puntatore del mouse sui contesti del metodo, notare il triangolo nero a destra. Facendo clic su tale triangolo viene visualizzato anche il menu di scelta rapida.

Per le applicazioni di grandi dimensioni con molti thread, è possibile concentrarsi solo su un subset di thread. La finestra Stack paralleli può visualizzare gli stack di chiamate solo per i thread contrassegnati. Per contrassegnare i thread, usare 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 della finestra stack paralleli e del tooltip.

Finestra Stack paralleli e suggerimento

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 nel punto di interruzione corrente è S.L, che contiene tre thread e viene visualizzato in tre caselle. Fare doppio clic su S.L.

    Screenshot del percorso di esecuzione nella finestra Stack paralleli.

    Percorso di esecuzione nella finestra Stack Paralleli

    Si noti che S.L è in grassetto nelle altre due caselle in modo che sia possibile vedere dove appare. Per visualizzare i frame che chiamano in S.L. e i frame in cui S.L. chiama, selezionare il pulsante Attiva/Disattiva visualizzazione metodo sulla barra degli strumenti. La seguente illustrazione mostra la vista del metodo della finestra Stack Paralleli.

    Screenshot della visualizzazione Metodo nella finestra Stack paralleli.

    Visualizzazione dei metodi nella finestra Stack paralleli

    Si noti come il diagramma abbia ruotato sul metodo selezionato e lo abbia posizionato nel proprio riquadro al centro della vista. I riceventi 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 paralleli include anche gli altri elementi seguenti.

    • Display esadecimale attiva/disattiva i numeri nelle descrizioni comando tra il formato decimale ed esadecimale.

    • Le impostazioni dei simboli aprono 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. Prova a vedere il diagramma espandersi per accogliere gli altri riquadri (che potrebbero essere disattivati perché non hai simboli per questi).

  2. Nella finestra Stack paralleli verificare che il pulsante Scorrimento automatico fino allo stack frame corrente sulla barra degli strumenti sia attivo.

    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 paralleli scorrere fino a sinistra e fino al 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 scorre automaticamente nella posizione. È possibile cambiare i thread nella finestra Thread o cambiare stack di chiamate nella finestra Stack di chiamate e notare come la visualizzazione scorre automaticamente al fotogramma corretto. Disattivare l'opzione Scorrimento automatico fino all'opzione Frame dello strumento corrente e visualizzare la differenza.

    La vista a volo d'uccello aiuta anche con i grandi diagrammi nella finestra Stack Paralleli. 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 della vista a volo d'uccello nella finestra Stack paralleli.

    Visualizzazione degli occhi degli uccelli nella finestra Stack paralleli

    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 ingrandire e ridurre il diagramma, tenere premuto CTRL mentre si sposta la rotellina del mouse. In alternativa, selezionare il pulsante Zoom sulla barra degli strumenti e quindi usare lo strumento Zoom.

    È anche possibile visualizzare gli stack in una direzione dall'alto verso il basso anziché dal basso verso l'alto facendo clic sul menu Strumenti , scegliendo Opzioni e quindi selezionando o deselezionando l'opzione sotto il 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 degli Stack paralleli

Prima di continuare, è consigliabile 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. Sul menu Debug, posizionare il puntatore su Windows e quindi selezionare Thread. Ancorare la finestra Threads nella parte inferiore di Visual Studio.

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

  4. Fare doppio clic su un thread nella finestra Thread per renderlo corrente. I thread correnti hanno 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 di quattro attività in esecuzione nella finestra Attività.

    Quattro attività in esecuzione nella finestra Attività

    Per ogni attività in esecuzione, è possibile leggerne l'ID, che viene restituito dalla proprietà con lo stesso nome, oltre all'ID e al nome del thread che lo esegue, nonché alla relativa posizione (passando il mouse su di esso visualizza una finestra di suggerimento con l'intero stack delle chiamate). Inoltre, nella colonna Task è possibile visualizzare il metodo passato all'attività; in altre parole, il punto di partenza.

    È possibile ordinare qualsiasi colonna. Si noti il glifo di ordinamento che indica la colonna di ordinamento e la direzione. È anche possibile riordinare le colonne trascinandole verso sinistra o destra.

    La freccia gialla indica l'attività corrente. È possibile cambiare attività facendo doppio clic su un'attività o usando il menu di scelta rapida. Quando si cambia attività, il thread sottostante diventa corrente e le altre finestre vengono aggiornate.

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

    Quando si passa manualmente da un'attività a un'altra, la freccia gialla si sposta, ma una freccia bianca mostra ancora l'attività che ha causato l'interruzione del 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 di due attività in attesa nella finestra Attività.

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.

Due attività in attesa nella finestra Attività

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

Attività in attesa e tooltip nella finestra Attività

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

È possibile usare il contrassegno per tenere traccia delle attività tra punti di interruzione diversi nella stessa sessione di debug o per filtrare le attività i cui stack di chiamate vengono visualizzati nella finestra Stack paralleli .

Quando è stata usata la finestra Stack paralleli in precedenza, sono stati visualizzati i thread dell'applicazione. Visualizzare di nuovo la finestra Stack paralleli , ma questa volta visualizzare le attività dell'applicazione. A tale scopo, selezionare Attività nella casella in alto a sinistra. La figura seguente mostra la vista attività.

Screenshot della visualizzazione delle Attività nella finestra Stack paralleli.

Vista attività nella finestra 'Stack paralleli'

I thread che attualmente non eseguono attività non sono mostrati 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 per aprire il menu di scelta rapida relativo alla colonna.

È possibile usare il menu di scelta rapida per aggiungere o rimuovere colonne. Ad esempio, la colonna AppDomain non è selezionata; pertanto, non viene visualizzato nell'elenco. Selezionare Padre. La colonna Padre viene visualizzata senza valori per una delle 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 della visualizzazione Padre-figlio nella finestra Attività.

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; vedere queste informazioni qui è un altro vantaggio della finestra Attività. Per confermare questo, consultare la finestra Stack paralleli. Assicurati di visualizzare Attività. È possibile individuare le attività 11 e 12 esaminando le descrizioni comandi nella finestra Stack Paralleli.

Visualizzazione attività nella finestra Stack paralleli

Una nuova attività, l'attività 5, è ora in esecuzione e l'attività 4 è ora in attesa. È possibile vedere perché passando il puntatore del 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. Verrà visualizzata la figura seguente.

Visualizzazione padre-figlio nella finestra Attività

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; vedere queste informazioni qui è un altro vantaggio della finestra Attività. Per confermare questo, consultare la finestra Stack paralleli. Assicurati di visualizzare Attività. Individuare le attività 4 e 5 facendo doppio clic su di esse nella finestra Attività . Quando si esegue questa operazione, l'evidenziazione blu nella finestra Stack paralleli viene aggiornata. È anche possibile individuare le attività 4 e 5 scansionando i suggerimenti nella finestra Stack Paralleli.

Visualizzazione attività nella finestra Stack paralleli

Nella finestra Parallel Stacks, fare clic con il pulsante destro del mouse su S.P e quindi selezionare Vai al thread. La finestra passa alla visualizzazione Thread e la cornice corrispondente è visualizzata. È possibile visualizzare entrambe le attività nello stesso thread.

Thread evidenziato nella vista dei thread

Questo è un altro vantaggio della Visualizzazione Attività nella finestra Stack paralleli, 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. Verrà visualizzata la figura seguente.

Screenshot di quattro stati delle attività nella finestra degli Stack Paralleli.

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.

Quattro stati dell'attività nella finestra Stack paralleli

Poiché l'attività 5 è stata completata, non viene più visualizzata. Se questa situazione non si verifica sul tuo computer e il deadlock non viene visualizzato, premi F11 una volta.

L'attività 3 e l'attività 4 sono ora in attesa l'una sull'altra e sono bloccate. Sono inoltre disponibili 5 nuove attività collegate all'attività 2 e sono ora pianificate. Le attività pianificate sono attività avviate nel codice ma non ancora eseguite. Di conseguenza, le colonne Location e Thread Assignment sono vuote.

Visualizza di nuovo la finestra Stack paralleli. L'intestazione di ogni riquadro include un tooltip che mostra gli ID e i nomi dei thread. Passare alla visualizzazione delle attività nella finestra Stack paralleli. Passare il puntatore del mouse su un'intestazione per visualizzare l'ID e il nome dell'attività e lo stato dell'attività, come illustrato nella figura seguente.

Suggerimento intestazione della finestra Stacks paralleli

È possibile raggruppare le attività per colonna. 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 delle attività raggruppate nella finestra Attività.

Attività raggruppate nella finestra Attività

È anche possibile raggruppare in base a qualsiasi altra colonna. Raggruppando le attività, è possibile concentrarsi su un subset di attività. Ogni gruppo collassabile ha un conteggio degli elementi che sono 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 attività oppure bloccare tutti i thread ad 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.

Riassunto

Questa procedura dettagliata ha illustrato le finestre del debugger Parallel Tasks e Parallel Stacks . Usare queste finestre su progetti reali che usano codice multithreading. È possibile esaminare il codice parallelo scritto in C++, C# o Visual Basic.