Condividi tramite


Procedura dettagliata: modifica di componenti multithreading semplici con Visual C#

Benché il componente BackgroundWorker sostituisca lo spazio dei nomi System.Threading aggiungendovi funzionalità, lo spazio dei nomi System.Threading viene mantenuto per compatibilità con le versioni precedenti e per un eventuale utilizzo futuro. Per ulteriori informazioni, vedere Cenni preliminari sul componente BackgroundWorker.

È possibile scrivere applicazioni che consentono di eseguire più attività contemporaneamente. Questa caratteristica, detta multithreading o free threading, è il sistema ideale per progettare componenti che utilizzano il processore in modo intensivo e richiedono l'input dell'utente. Il multithreading può ad esempio essere sfruttato da un componente che calcola i dati relativi ai fogli paga. Il componente è in grado di elaborare i dati immessi in un database da un utente su un thread mentre i calcoli degli stipendi, che richiedono un utilizzo intensivo del processore, vengono eseguiti su un altro thread. Eseguendo i processi su thread separati, gli utenti non dovranno attendere che il computer completi i calcoli per immettere nuovi dati. In questa procedura dettagliata, verrà creato un componente semplice con multithreading che esegue diversi calcoli complessi contemporaneamente.

Creazione del progetto

L'applicazione è costituita da un unico form e un componente. L'utente immetterà i valori, segnalando al componente di avviare i calcoli. Il form riceverà i valori dal componente e li visualizzerà nei controlli label. Il componente eseguirà quindi i calcoli, che richiedono un utilizzo intensivo del processore, e ne segnalerà il completamento al form. Sarà necessario creare delle variabili pubbliche nel componente per contenere i valori ricevuti dall'interfaccia utente e si dovranno inoltre implementare i metodi per l'esecuzione dei calcoli sulla base dei valori di tali variabili.

Nota

Sebbene in genere sia preferibile utilizzare una funzione per un metodo che calcola un valore, gli argomenti non possono essere passati da un thread all'altro e non è possibile ottenere la restituzione di valori.Esistono molti sistemi semplici per fornire valori ai thread e ricevere valori da questi ultimi.In questa dimostrazione, la restituzione dei valori all'interfaccia utente verrà ottenuta mediante l'aggiornamento delle variabili pubbliche e si utilizzeranno eventi per notificare al programma principale il completamento dell'esecuzione da parte di un thread.

È possibile che le finestre di dialogo e i comandi di menu visualizzati siano diversi da quelli descritti nella Guida a seconda delle impostazioni attive o dell'edizione del programma.Per modificare le impostazioni, scegliere Importa/esporta impostazioni dal menu Strumenti.Per ulteriori informazioni, vedere Personalizzazione delle impostazioni di sviluppo in Visual Studio.

Per creare il form

  1. Creazione di un nuovo progetto Applicazione Windows.

  2. Denominare l'applicazione Calculations e modificare il nome di Form1.cs in frmCalculations.cs Quando in Visual Studio viene richiesto di rinominare l'elemento di codice Form1, scegliere .

    Il form fungerà da interfaccia utente primaria per l'applicazione.

  3. Aggiungere al form cinque controlli Label, quattro controlli Button e un controllo TextBox.

  4. Impostare le proprietà per questi controlli come indicato di seguito.

    Controllo

    Nome

    Text

    label1

    lblFactorial1

    (vuoto)

    label2

    lblFactorial2

    (vuoto)

    label3

    lblAddTwo

    (vuoto)

    label4

    lblRunLoops

    (vuoto)

    label5

    lblTotalCalculations

    (vuoto)

    button1

    btnFactorial1

    Factorial

    button2

    btnFactorial2

    Factorial - 1

    button3

    btnAddTwo

    Add Two

    button4

    btnRunLoops

    Run a Loop

    textBox1

    txtValue

    (vuoto)

Per creare il componente Calculator

  1. Scegliere Aggiungi componente dal menu Progetto.

  2. Denominare il componente Calculator.

Per aggiungere variabili pubbliche al componente Calculator

  1. Aprire l'editor di codice per Calculator.

  2. Aggiungere le istruzioni necessarie per la creazione delle variabili pubbliche che verranno utilizzate per passare i valori da frmCalculations a ogni thread.

    La variabile varTotalCalculations manterrà il conteggio dei calcoli eseguiti dal componente, mentre le altre variabili riceveranno valori dal form.

    public int varAddTwo; 
    public int varFact1;
    public int varFact2;
    public int varLoopValue;
    public double varTotalCalculations = 0;
    

Per aggiungere metodi ed eventi al componente Calculator

  1. Dichiarare i delegati per gli eventi che verranno utilizzati dal componente per comunicare i valori al form.

    Nota

    Anche se si dichiarano quattro eventi, è necessario creare solo tre delegati, in quanto due eventi avranno la stessa firma.

    Immediatamente sotto le dichiarazioni delle variabili immesse nel passaggio precedente, digitare il seguente codice:

    // This delegate will be invoked with two of your events.
    public delegate void FactorialCompleteHandler(double Factorial, double TotalCalculations);
    public delegate void AddTwoCompleteHandler(int Result, double TotalCalculations);
    public delegate void LoopCompleteHandler(double TotalCalculations, int Counter);
    
  2. Dichiarare gli eventi che il componente utilizzerà per comunicare con l'applicazione. Eseguire questa operazione aggiungendo il codice riportato di seguito immediatamente sotto il codice immesso nel passaggio precedente.

    public event FactorialCompleteHandler FactorialComplete;
    public event FactorialCompleteHandler FactorialMinusOneComplete;
    public event AddTwoCompleteHandler AddTwoComplete;
    public event LoopCompleteHandler LoopComplete;
    
  3. Immediatamente sotto il codice immesso nel passaggio precedente, digitare il seguente codice:

    // This method will calculate the value of a number minus 1 factorial
    // (varFact2-1!).
    public void FactorialMinusOne()
    {
       double varTotalAsOfNow = 0;
       double varResult = 1;
       // Performs a factorial calculation on varFact2 - 1.
       for (int varX = 1; varX <= varFact2 - 1; varX++)
       {
          varResult *= varX;
          // Increments varTotalCalculations and keeps track of the current 
          // total as of this instant.
          varTotalCalculations += 1;
          varTotalAsOfNow = varTotalCalculations;
       }
       // Signals that the method has completed, and communicates the 
       // result and a value of total calculations performed up to this 
       // point.
       FactorialMinusOneComplete(varResult, varTotalAsOfNow);
    }
    
    // This method will calculate the value of a number factorial.
    // (varFact1!)
    public void Factorial()
    {
       double varResult = 1;
       double varTotalAsOfNow = 0;
       for (int varX = 1; varX <= varFact1; varX++)
       {
          varResult *= varX;
          varTotalCalculations += 1;
          varTotalAsOfNow = varTotalCalculations;
       }
       FactorialComplete(varResult, varTotalAsOfNow);
    }
    
    // This method will add two to a number (varAddTwo+2).
    public void AddTwo()
    {
       double varTotalAsOfNow = 0;  
       int varResult = varAddTwo + 2;
       varTotalCalculations += 1;
       varTotalAsOfNow = varTotalCalculations;
       AddTwoComplete(varResult, varTotalAsOfNow);
    }
    
    // This method will run a loop with a nested loop varLoopValue times.
    public void RunALoop()
    {
       int varX;
       double varTotalAsOfNow = 0;
       for (varX = 1; varX <= varLoopValue; varX++)
       {
        // This nested loop is added solely for the purpose of slowing down
        // the program and creating a processor-intensive application.
          for (int varY = 1; varY <= 500; varY++)
          {
             varTotalCalculations += 1;
             varTotalAsOfNow = varTotalCalculations;
          }
       }
       LoopComplete(varTotalAsOfNow, varLoopValue);
    }
    

Trasferimento dell'input dell'utente al componente

Il passaggio successivo prevede l'aggiunta a frmCalculations del codice che consente di ricevere l'input dell'utente e di trasferire e ricevere valori da e verso il componente Calculator.

Per implementare la funzionalità front-end in frmCalculations

  1. Aprire frmCalculations nell'editor di codice.

  2. Individuare l'istruzione public partial class frmCalculations. Immediatamente sotto { digitare:

    Calculator Calculator1;
    
  3. Individuare il costruttore. Immediatamente prima di } aggiungere la riga seguente:

    // Creates a new instance of Calculator.
    Calculator1 = new Calculator();
    
  4. Nella finestra di progettazione fare clic su ciascun controllo button per generare la struttura di codice per ciascun gestore eventi Click del controllo, quindi aggiungere il codice per creare i gestori.

    Al termine, il codice dei gestori eventi Click dovrebbe risultare simile al seguente:

    // Passes the value typed in the txtValue to Calculator.varFact1.
    private void btnFactorial1_Click(object sender, System.EventArgs e)
    {
       Calculator1.varFact1 = int.Parse(txtValue.Text);
       // Disables the btnFactorial1 until this calculation is complete.
       btnFactorial1.Enabled = false;
       Calculator1.Factorial();
    }
    
    private void btnFactorial2_Click(object sender, System.EventArgs e)
    {
       Calculator1.varFact2 = int.Parse(txtValue.Text);
       btnFactorial2.Enabled = false;
       Calculator1.FactorialMinusOne();
    }
    private void btnAddTwo_Click(object sender, System.EventArgs e)
    {
       Calculator1.varAddTwo = int.Parse(txtValue.Text);
       btnAddTwo.Enabled = false;
       Calculator1.AddTwo();
    }
    private void btnRunLoops_Click(object sender, System.EventArgs e)
    {
       Calculator1.varLoopValue = int.Parse(txtValue.Text);
       btnRunLoops.Enabled = false;
       // Lets the user know that a loop is running
       lblRunLoops.Text = "Looping";
       Calculator1.RunALoop();
    }
    
  5. Dopo il codice aggiunto nel passaggio precedente, aggiungere il seguente codice per gestire gli eventi che il form riceverà da Calculator1:

    private void FactorialHandler(double Value, double Calculations)
    // Displays the returned value in the appropriate label.
    {
       lblFactorial1.Text = Value.ToString();
       // Re-enables the button so it can be used again.
       btnFactorial1.Enabled = true;
       // Updates the label that displays the total calculations performed
       lblTotalCalculations.Text = "TotalCalculations are " + 
          Calculations.ToString();
    }
    
    private void FactorialMinusHandler(double Value, double Calculations)
    {
       lblFactorial2.Text = Value.ToString();
       btnFactorial2.Enabled = true;
       lblTotalCalculations.Text = "TotalCalculations are " + 
          Calculations.ToString();
    }
    
    private void AddTwoHandler(int Value, double Calculations)
    {
       lblAddTwo.Text = Value.ToString();
       btnAddTwo.Enabled = true;
       lblTotalCalculations.Text = "TotalCalculations are " +
          Calculations.ToString();
    }
    
    private void LoopDoneHandler(double Calculations, int Count)
    {
       btnRunLoops.Enabled = true;
       lblRunLoops.Text = Count.ToString();
       lblTotalCalculations.Text = "TotalCalculations are " +
          Calculations.ToString();
    }
    
  6. Nel costruttore di frmCalculations aggiungere il seguente codice immediatamente prima di } per gestire gli eventi personalizzati che il form riceverà da Calculator1.

    Calculator1.FactorialComplete += new
       Calculator.FactorialCompleteHandler(this.FactorialHandler);
    Calculator1.FactorialMinusOneComplete += new
       Calculator.FactorialCompleteHandler(this.FactorialMinusHandler);
    Calculator1.AddTwoComplete += new
       Calculator.AddTwoCompleteHandler(this.AddTwoHandler);
    Calculator1.LoopComplete += new
       Calculator.LoopCompleteHandler(this.LoopDoneHandler);
    

Test dell'applicazione

A questo punto è stato creato un progetto che incorpora un form e un componente in grado di eseguire diversi calcoli complessi. Sebbene la funzionalità di multithreading non sia stata ancora implementata, è consigliabile testare il progetto per verificarne il funzionamento prima di continuare.

Per verificare il progetto

  1. Scegliere Avvia debug dal menu Debug.

    L'applicazione viene avviata e viene visualizzato frmCalculations.

  2. Nella casella di testo digitare 4 quindi fare clic sul pulsante Add Two.

    Nell'etichetta sottostante dovrebbe apparire il numero "6", mentre in lblTotalCalculations dovrebbe essere visualizzata l'indicazione "Total Calculations are 1".

  3. Fare clic sul pulsante con etichetta Factorial - 1.

    Sotto il pulsante dovrebbe venire visualizzato il numero "6" e l'indicazione in lblTotalCalculations dovrebbe essere "Total Calculations are 4".

  4. Digitare 20 per modificare il valore della casella di testo, quindi fare clic sul pulsante Factorial.

    Nell'etichetta sottostante verrà visualizzato il numero "2.43290200817664E+18" e l'indicazione in lblTotalCalculations diventa "Total Calculations are 24".

  5. Digitare 50000 per modificare il valore della casella di testo, quindi fare clic sul pulsante Run A Loop.

    Si noti che sarà necessario attendere brevemente che il pulsante venga nuovamente abilitato. L'etichetta sotto il pulsante indicherà "50000" e il totale dei calcoli visualizzato sarà "25000024".

  6. Digitare 5000000 per modificare il valore nella casella di testo, fare clic sul pulsante Run A Loop quindi fare subito clic sul pulsante Add Two. Fare di nuovo clic sul pulsante.

    Il pulsante non risponderà, analogamente a qualsiasi altro controllo del form, finché i cicli non saranno completati.

    Se nel programma viene eseguito un singolo thread di esecuzione, i calcoli che richiedono un utilizzo intensivo del processore, riportati nell'esempio precedente, tenderanno a bloccare il programma fino a quando non verranno completati. Nella sezione successiva verrà aggiunta la funzionalità multithreading all'applicazione in modo da consentire l'esecuzione simultanea di più thread.

Aggiunta della funzionalità multithreading

Nell'esempio precedente sono state illustrate le limitazioni delle applicazioni con un singolo thread di esecuzione. Nella sezione successiva si utilizzerà la classe Thread per aggiungere più thread di esecuzione al componente.

Per aggiungere la subroutine Threads

  1. Aprire Calculator.cs nell'editor di codice.

  2. Nella parte superiore del codice individuare la dichiarazione di classe e immediatamente sotto { digitare il seguente codice:

    // Declares the variables you will use to hold your thread objects.
    public System.Threading.Thread FactorialThread; 
    public System.Threading.Thread FactorialMinusOneThread;  
    public System.Threading.Thread AddTwoThread; 
    public System.Threading.Thread LoopThread;
    
  3. Immediatamente prima della fine della dichiarazione di classe nella parte inferiore del codice, aggiungere il seguente metodo:

    public void ChooseThreads(int threadNumber)
    {
    // Determines which thread to start based on the value it receives.
    switch(threadNumber)
       {
          case 1:
             // Sets the thread using the AddressOf the subroutine where
             // the thread will start.
             FactorialThread = new System.Threading.Thread(new
                System.Threading.ThreadStart(this.Factorial));
             // Starts the thread.
             FactorialThread.Start();
             break;
          case 2:
             FactorialMinusOneThread = new
                System.Threading.Thread(new
                   System.Threading.ThreadStart(this.FactorialMinusOne));
             FactorialMinusOneThread.Start();
             break;
          case 3:
             AddTwoThread = new System.Threading.Thread(new
                 System.Threading.ThreadStart(this.AddTwo));
             AddTwoThread.Start();
             break;
          case 4:
             LoopThread = new System.Threading.Thread(new
                System.Threading.ThreadStart(this.RunALoop));
             LoopThread.Start();
             break;
       }
    }
    

    La creazione di un'istanza di un oggetto Thread richiede un argomento sotto forma di oggetto ThreadStart. L'oggetto ThreadStart è un delegato che fa riferimento all'indirizzo del metodo in cui il thread verrà avviato. Poiché l'oggetto ThreadStart non può accettare parametri né passare valori, può solo indicare un metodo void. Il metodo ChooseThreads appena implementato riceverà un valore dal programma dal quale viene chiamato e utilizzerà tale valore per determinare il thread appropriato da avviare.

Per aggiungere il codice appropriato a frmCalculations

  1. Aprire frmCalculator.cs nell'editor di codice, quindi individuare private void btnFactorial1_Click.

    1. Impostare come commento la riga di chiamata al metodo Calculator1.Factorial1 nel seguente modo:

      // Calculator1.Factorial()
      
    2. Per chiamare il metodo Calculator1.ChooseThreads aggiungere la seguente riga alla chiamata:

      // Passes the value 1 to Calculator1, thus directing it to start the 
      // correct thread.
      Calculator1.ChooseThreads(1);
      
  2. Apportare modifiche analoghe agli altri metodi button_click.

    Nota

    Accertarsi di includere il valore corretto per l'argomento Threads.

    Al termine, il codice dovrebbe risultare simile al seguente:

    private void btnFactorial1_Click(object sender, System.EventArgs e)
    // Passes the value typed in the txtValue to Calculator.varFact1
    {
       Calculator1.varFact1 = int.Parse(txtValue.Text);
       // Disables the btnFactorial1 until this calculation is complete
       btnFactorial1.Enabled = false;
       // Calculator1.Factorial();
       Calculator1.ChooseThreads(1);
    }
    
    private void btnFactorial2_Click(object sender, System.EventArgs e)
    {
       Calculator1.varFact2 = int.Parse(txtValue.Text); 
       btnFactorial2.Enabled = false;         
       // Calculator1.FactorialMinusOne();
       Calculator1.ChooseThreads(2);
    }
    private void btnAddTwo_Click(object sender, System.EventArgs e)
    {
       Calculator1.varAddTwo = int.Parse(txtValue.Text);
       btnAddTwo.Enabled = false;
       // Calculator1.AddTwo();
       Calculator1.ChooseThreads(3);
    }
    
    private void btnRunLoops_Click(object sender, System.EventArgs e)
    {
       Calculator1.varLoopValue = int.Parse(txtValue.Text);
       btnRunLoops.Enabled = false;
       // Lets the user know that a loop is running
       lblRunLoops.Text = "Looping";
       // Calculator1.RunALoop();
       Calculator1.ChooseThreads(4);
    }
    

Chiamate di marshalling ai controlli

In questa sezione verrà illustrato come agevolare l'aggiornamento degli elementi visualizzati nel form. Poiché i controlli sono sempre di proprietà del thread di esecuzione principale, qualsiasi chiamata effettuata a un controllo da un thread subordinato richiede una chiamata di marshalling. Il marshalling implica lo spostamento di una chiamata da un limite all'altro del thread ed è un processo piuttosto dispendioso in termini di risorse. Per ridurre al minimo l'entità del marshalling e garantire che le chiamate vengano gestite in modo indipendente dal thread, si utilizzerà il metodo Control.BeginInvoke per richiamare i metodi del thread di esecuzione principale. Questo tipo di chiamata è necessaria quando si chiamano metodi che modificano i controlli. Per informazioni dettagliate, vedere Procedura: modificare i controlli dai thread.

Per creare procedure di richiamo dei controlli

  1. Aprire l'editor di codice per frmCalculations Nella sezione delle dichiarazioni aggiungere il seguente codice:

    public delegate void FHandler(double Value, double Calculations);
    public delegate void A2Handler(int Value, double Calculations);
    public delegate void LDHandler(double Calculations, int Count);
    

    Invoke e BeginInvoke richiedono come argomento un delegato per il metodo appropriato. In queste righe vengono dichiarate le firme dei delegati che verranno utilizzate da BeginInvoke per richiamare i metodi appropriati.

  2. Aggiungere al codice i seguenti metodi vuoti:

    public void FactHandler(double Value, double Calculations)
    {
    }
    public void Fact1Handler(double Value, double Calculations)
    {
    }
    public void Add2Handler(int Value, double Calculations)
    {
    }
    public void LDoneHandler(double Calculations, int Count)
    {
    }
    
  3. Utilizzare i comandi Taglia e Copia del menu Modifica per tagliare tutto il codice dal metodo FactorialHandler e incollarlo in FactHandler.

  4. Ripetere il passaggio precedente per FactorialMinusHandler e Fact1Handler, AddTwoHandler e Add2Handler, e LoopDoneHandler e LDoneHandler.

    Al termine, non vi sarà codice rimanente in FactorialHandler, Factorial1Handler, AddTwoHandler e LoopDoneHandler e tutto il codice precedentemente contenuto in tali metodi sarà stato spostato nei nuovi metodi.

  5. Chiamare il metodo BeginInvoke per richiamare i metodi in modo asincrono. È possibile chiamare il metodo BeginInvoke dal form (this) o da qualsiasi controllo del form.

    Al termine, il codice dovrebbe risultare simile al seguente:

    protected void FactorialHandler(double Value, double Calculations)
    {
       // BeginInvoke causes asynchronous execution to begin at the address
       // specified by the delegate. Simply put, it transfers execution of 
       // this method back to the main thread. Any parameters required by 
       // the method contained at the delegate are wrapped in an object and 
       // passed. 
       this.BeginInvoke(new FHandler(FactHandler), new Object[]
          {Value, Calculations});
    }
    protected void FactorialMinusHandler(double Value, double Calculations)
    {
       this.BeginInvoke(new FHandler(Fact1Handler), new Object []
          {Value, Calculations});
    }
    
    protected void AddTwoHandler(int Value, double Calculations)
    {
       this.BeginInvoke(new A2Handler(Add2Handler), new Object[]
          {Value, Calculations});
    }
    
    protected void LoopDoneHandler(double Calculations, int Count)
    {
       this.BeginInvoke(new LDHandler(LDoneHandler), new Object[]
          {Calculations, Count});
    }
    

    Apparentemente il gestore eventi effettua semplicemente una chiamata al metodo successivo, ma in realtà viene richiamato un metodo sul thread principale dell'operazione. Questo tipo di approccio consente di ridurre il numero di chiamate effettuate oltre i limiti del thread e di eseguire le applicazioni con multithreading in modo efficiente senza causare problemi di blocco del sistema. Per dettagli sull'utilizzo di un ambiente con multithreading, vedere Procedura: modificare i controlli dai thread.

  6. Salvare il lavoro.

  7. Eseguire il test della soluzione scegliendo Avvia debug dal menu Debug.

    1. Digitare 10000000 nella casella di testo e fare clic su Run A Loop.

      Nell'etichetta sotto il pulsante viene visualizzato "Looping". L'esecuzione di questo ciclo dovrebbe richiedere parecchio tempo. Se viene completato troppo presto, modificare il numero di conseguenza.

    2. Fare clic su tutti e tre i pulsanti ancora attivi in rapida successione. Tutti i pulsanti risponderanno all'input. Il primo risultato viene visualizzato nell'etichetta sotto il pulsante Add Two. I risultati successivi verranno visualizzati nelle etichette sotto i pulsanti Factorial. I risultati restituiti corrispondono a un numero infinito, in quanto il numero restituito da un Factorial pari a 10,000,000 è troppo grande per essere contenuto in una variabile in precisione doppia. Infine, dopo un ulteriore ritardo, i risultati vengono restituiti e visualizzati anche sotto il pulsante Run A Loop.

      Come si è potuto osservare, quattro diversi calcoli sono stati eseguiti contemporaneamente su quattro thread distinti, l'interfaccia utente ha continuato a rispondere all'input e i risultati sono stati restituiti al completamento di ciascun thread.

Coordinamento dei thread

L'utente esperto di applicazioni con multithreading potrebbe notare un lieve errore dovuto al codice così come è stato creato. Richiamare le righe di codice da ciascun metodo per l'elaborazione dei calcoli in Calculator:

varTotalCalculations += 1;
varTotalAsOfNow = varTotalCalculations;

Queste due righe di codice consentono di incrementare la variabile pubblica varTotalCalculations e di impostare la variabile locale varTotalAsOfNow su tale valore. Il valore viene quindi restituito a frmCalculations e visualizzato in un controllo label. Il problema è stabilire se viene restituito il valore corretto. La risposta è affermativa nel caso in cui venga utilizzato un solo thread di esecuzione. Se al contrario si utilizzano più thread, il valore restituito potrebbe non essere corretto. Ogni thread, infatti, incrementa la variabile varTotalCalculations ed è possibile che, se un thread ha incrementato la variabile ma non ne ha ancora copiato il valore in varTotalAsOfNow, un altro thread incrementi anch'esso il valore della variabile. È quindi possibile che ciascun thread restituisca risultati imprecisi. Visual C# fornisce la Istruzione lock (Riferimenti per C#) per consentire la sincronizzazione dei thread e garantire che ciascun thread restituisca sempre un risultato preciso. La sintassi per lock è la seguente:

lock(AnObject)
{
   // Insert code that affects the object.
   // Insert more code that affects the object.
   // Insert more code that affects the object.
// Release the lock.
}

Quando viene attivato il blocco lock l'esecuzione dell'espressione specificata viene bloccata finché per il thread specificato è presente un blocco esclusivo dell'oggetto in questione. Nell'esempio riportato sopra, l'esecuzione viene bloccata su AnObject. È necessario utilizzare lock con un oggetto che restituisca un riferimento anziché un valore. L'esecuzione può quindi precedere come blocco senza interferenze da parte di altri thread. Un insieme di istruzioni che vengono eseguite come unità costituisce un'operazione inscindibile. Quando si raggiunge il simbolo }, l'espressione viene liberata e viene consentita la normale esecuzione dei thread.

Per aggiungere l'istruzione lock all'applicazione

  1. Aprire Calculator.cs nell'editor di codice.

  2. Individuare tutte le istanze del seguente codice:

    varTotalCalculations += 1;
    varTotalAsOfNow = varTotalCalculations;
    

    Dovrebbero essere presenti quattro istanze, una per ciascun metodo di calcolo.

  3. Modificare il codice per ogni istanza nel seguente modo:

    lock(this)
    {
       varTotalCalculations += 1;
       varTotalAsOfNow = varTotalCalculations;
    }
    
  4. Salvare il lavoro ed eseguirne il test come nell'esempio precedente.

    Si noterà un lieve calo delle prestazioni del programma in quanto l'esecuzione dei thread viene interrotta quando si ottiene un blocco esclusivo del componente. Questo tipo di approccio, pur garantendo la precisione, annulla alcuni dei vantaggi offerti dal multithreading in termini di prestazioni. Si consiglia quindi di valutare attentamente la necessità di bloccare i thread e di implementare i blocchi solo quando è assolutamente necessario.

Vedere anche

Attività

Procedura: coordinare più thread di esecuzione

Procedura dettagliata: modifica di componenti multithreading semplici con Visual Basic

Riferimenti

BackgroundWorker

Concetti

Cenni preliminari sul modello asincrono basato su eventi

Altre risorse

Programmazione con i componenti

Procedure dettagliate sulla programmazione dei componenti

Multithreading nei componenti