Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Multithreading in ASP.NET 2

Introduzione all'uso del thread pool di IIS per la programmazione concorrente
Introduzione all'uso del thread pool di IIS per la programmazione concorrente
Link copiato negli appunti

Il .NET Framework fornisce la possibilità di creare applicativi multithreaded. In questo articolo viene spiegato il concetto di Multithreading e le tecniche per poter gestire i thread in maniera efficace e semplice. In particolare, vedremo come eseguire una procedura in un thread separato aggiungendo questo thread ad un thread pool.

Cos'è il Multithreading

Il Multithreading è quella caratteristica (tipica dei sistemi operativi) che permette ad una applicazione di eseguire una o più azioni (thread) in parallelo.

Nel caso specifico è utile se vogliamo evitare che il thread corrispondente ad una richiesta, venga bloccato finché questa venga portata a termine. In una applicazione "Multithreaded" possiamo svolgere più attività (o task) contemporaneamente: l'azione successiva può essere svolta senza dover attendere che quella precedente venga completata. Ad esempio nei programmi di elaborazione testi (come Word), mentre digitiamo il testo viene svolto anche il controllo dell'ortografia e il salvataggio del documento ad intervalli regolari di tempo.

Per comprenderne il funzionamento esaminiamo un esempio con un singolo thread.

Listato 1. Codice che non sfrutta il multithreading

Class _Default Inherits System.Web.UI.Page

  Public Sub Sub1()
    Dim i As Integer
    For i = 1 To 5
      Response.Write("Sub1() </br>")
    Next
  End Sub

  Public Sub Sub2()
    Dim i As Integer
    For i = 6 To 10
    Response.Write("Sub2() </br>")
  Next

End Sub

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
  Sub1()
  Sub2()
  Response.Write("</br> ---- FINE ----")
End Sub

End Class

Figura 1. Esecuzione sequenziale
Esecuzione sequenziale

La Sub2() viene eseguita solamente quando Sub1() ha completato la sua esecuzione. Questo perchè quando una procedura o una funzione viene richiamata il controllo dell'esecuzione viene trasferito all'interno della funzione (o procedura).

Quando termina l'esecuzione della procedura, l'applicazione riparte dalla linea di codice successiva alla linea che ha richiamato la procedura.

Utilizzando il Multithreading, possiamo eseguire due thread concorrenti (in parallelo) all'interno della stessa applicazione. Questa tecnica diventa indispensabile quando si eseguono operazioni molto lunghe e laboriose che richiedono elevati carichi di lavoro come:

  • query complesse su database
  • operazioni di Input/Output
  • chiamate a web service remoti

Se una di queste operazioni, impiega un intervallo di tempo che viene considerato inaccettabile, è buona regola ricorrere al multithreading, altrimenti il thread corrispondente alla richiesta, viene bloccato fin quando la medesima richiesta non viene portata a termine.

In questo modo, l'interfaccia della nostra applicazione non viene bloccata e si possono quindi eseguire altre operazioni, mentre la funzione che richiede tempi di esecuzione lunghi continua ad essere eseguita in background.

È importante ricordare che bisogna prestare la massima attenzione quando si lavora con il multithreading perché in questo tipo di applicazione i bug sono difficili da intercettare e la creazione e la distruzione dei thread possono anche decrementare le performance della nostra applicazione se sono mal gestiti.

Il Thread Pool

Il .NET Framework fornisce la possibilità di creare applicativi multithreaded e, attraverso IIS (Internet Information Service), rende disponibile ad ogni applicazione ASP.NET un thread pool.

Per evitare la creazione di un thread per ogni singola richiesta con il thread pool si utilizza un insieme finito di thread da cui prelevare i vari thread per eseguire le richieste.

Quindi, non è necessario creare un nuovo thread, perché quando ci occorre, la nostra applicazione ne otterrà uno dal thread pool.

Non appena il thread completa la sua esecuzione verrà restituito al thread pool in attesa di essere riutilizzato per una nuova operazione, anziché essere distrutto.

Questo riutilizzo rende la nostra applicazione più performante e riduce il costo di un eccessiva creazione e distruzione di thread.

Un esempio pratico

Eseguiamo in un thread separato del thread pool, un'operazione potenzialmente lunga. Creiamo una nuova applicazione web e una pagina che chiamiamo "default.aspx".

La prima cosa da fare è includere il namespace System.Threading per poter creare i nuovi thread, mentre il namespace System.IO ci occorre per poter effettuare le operazioni di lettura/scrittura su file.

Imports System.Threading
Imports System.IO

Ora creiamo la procedura che verrà eseguita in background in un thread separato visto che potrebbe impegnare molta memoria.

Listato 2. Procedura "impegnativa"

Private Sub Scrivi100MilaRighe(ByVal s As Object)
  Dim i As Integer
  Dim sw As StreamWriter = New StreamWriter("c:taskNuovo.txt")

  For i = 0 To 100000
    sw.WriteLine("questa è una riga" & " - Data: " + DateTime.Now.ToString() & _
            " millisecondi:" & DateTime.Now.Millisecond.ToString())
  Next

  sw.WriteLine(" - FINE : " + DateTime.Now.ToString() & " millisecondi:" & DateTime.Now.Millisecond.ToString())
  sw.Close()
End Sub

Trasciniamo un pulsante nella nostra pagina e lo chiamiamo "Button1" e sul click del pulsante aggiungiamo il codice che lancia l'esecuzione della procedura Scrivi1000MilaRighe() in un thread del pool e poi stampa a video un messaggio.

Listato 2. Pagina multithread

Class _Default Inherits System.Web.UI.Page

  Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
    If ThreadPool.QueueUserWorkItem(AddressOf Scrivi100MilaRighe) Then
      Label2.Text = " Accodato nel thread pool </br>" & _
              DateTime.Now.ToString() & " millisecondi:" & DateTime.Now.Millisecond.ToString()
    Else
      Label2.Text = "Si è verificato un errore, impossibile accodare nel thread pool"
    End If
  End Sub

End Class

Abbiamo utilizzato la funzione ThreadPool.QueueUserWorkItem per eseguire la nostra procedura in un thread del thread pool. Il parametro passato alla funzione è la funzione che deve essere eseguita. Nel nostro caso è Scrivi100MilaRighe. La funzione viene eseguita quando un thread del pool di thread diventa disponibile. ThreadPool.QueueUserWorkItem restituisce il valore true se l'operazione di accodamento ha avuto successo.

Figura 2. L'applicazione multithreaded in esecuzione
L'applicazione multithreaded in esecuzione

Il thread è stato accodato nel thread pool ed è stato eseguito.

Nota: nel nostro esempio abbiamo utilizzato semplicemente l'operatore AddressOf per passare il metodo di callback a QueueUserWorkItem omettendo il costruttore WaitCallback perché Visual Basic chiama automaticamente il costruttore delegato corretto.

Il delegato WaitCallback rappresenta infatti il metodo di callback che deve essere eseguito da un thread del pool .

Ti consigliamo anche