Esercizio 1: Creazione Base e Sincronizzazione con `Join` Obiettivo: Comprendere come creare, avviare e attendere la terminazione di un thread. Richieste: Scrivere un programma che crea due thread • Il primo thread deve eseguire un metodo che stampa i numeri da 1 a 5, con una pausa di 1 secondo tra ogni numero. • Il secondo thread deve eseguire un metodo che stampa le lettere dalla 'A' alla 'E', con una pausa di 500 millisecondi tra ogni lettera. • Il thread principale (`Main`) deve avviare entrambi i thread e poi attendere che entrambi abbiano terminato il loro lavoro prima di stampare un messaggio finale ("Lavoro completato.") e chiudersi. • Utilizza il metodo `Join()` per garantire che il `Main` aspetti correttamente. Esercizio 2: Monitoraggio dello Stato di un Thread Obiettivo: Imparare a controllare lo stato di un thread durante la sua esecuzione. Richieste: Scrivere un programma che crea tre thread • Assegni un nome ad ogni thread (es. Primo, Secondo, Terzo) • Ogni thread stampa il proprio nome ed esegue un'operazione lunga che dura 10 secondi. • Il main stampa lo stato di ogni thread e o se non è "vivo" lo avvia thread. o Attende 3 secondo. o Stampa di nuovo lo stato di ogni thread e se è "vivo" ? Usa `Join()` per attendere la sua terminazione. • Stampa per l'ultima volta lo stato dei thread. • Analizza e commenta nel codice l'output che ti aspetti di vedere per ogni `Conso-le.WriteLine`. Esercizio 3: Foreground vs. Background Obiettivo: Comprendere la differenza fondamentale tra thread in foreground e in back-ground e il loro impatto sulla terminazione dell'applicazione. Richieste: Crea due thread, entrambi con un ciclo di lavoro che dura 5 secondi. • Imposta il primo thread come background. • Lascia il secondo thread come foreground. • Il thread principale (`Main`) deve avviare entrambi i thread e poi stampare un mes-saggio ("Il Main ha finito, l'applicazione si chiuderà?") e terminare immediatamente, senza usare `Join()`. • Esegui il programma e osserva il comportamento. Spiega perché l'applicazione rimane aperta e cosa viene stampato a schermo. • Modifica: Commenta la riga che avvia il thread in foreground. Esegui di nuovo il pro-gramma e descrivi la differenza nel comportamento di chiusura dell'applicazione. Esercizio 4: Combinazione di Concetti (Manager e Operaio) Obiettivo: Simulare uno scenario più complesso in cui un thread "manager" monitora un thread "operaio". Richieste: 1. Creare un thread operaio (in foreground) che simuli un lavoro di 10 secondi. 2. Creare un thread supervisore (in background) che, ogni 2 secondi, controlli lo stato del thread operaio e stampi un messaggio a console, come: `"[Supervisore]: Lo stato dell'operaio è: Running"`. 3. Il thread principale (`Main`) ha il ruolo di "manager": • Avvia sia il thread operaio che quello supervisore. • Attende la terminazione solo del thread operaio usando `Join()`. • Una volta che l'operaio ha finito, il `Main` stampa un messaggio finale ("L'operaio ha finito, il manager chiude la giornata.") e termina. 4. Spiega perché il thread supervisore si arresta automaticamente senza bisogno di un `Join()` specifico.