Browse Category by Guide
Guide

Thunderbird – Come Risolvere Errore Imap too Many Connection

Se stai cercando una soluzione valida e che ti consenta di risolvere il problema delle troppe connessioni simultanee al server imap di una casella di posta condivisa, sei nel posto giusto!

Dopo aver cambiato software per la gestione della posta, stiamo provando Mozilla Thunderbird che risulta molto versatile, comodo e gratis! Subito però abbiamo notato piccoli problemi con un indirizzo email configurato su computer multipli e con tantissime email. Solitamente il problema si presenta dopo aver inviato il messaggio e durante il tentativo di copia di quest’ultimo nella posta inviata.

Dopo alcune piccole indagini abbiamo scoperto che il nostro gestore di posta applica un massimo alle connessioni simultanee imap pari a 10 e il software utilizzato le utilizzava tutte. Tra le opzioni di configurazione di Thunderbird esiste la possibilità di mettere in Cache le connessioni e, di default, è impostato a 5. Riducendo questo ad 1 si dice a Thunderbird di mantenere aperta solo e soltanto una connessione garantendo che al più 10 utenti possano connettersi nello stesso momento.

Per modificare tale opzione è necessario: ”Click con tasto destro su un account -> Impostazioni account -> Impostazioni server -> Avanzate -> IMAP -> Numero massimo di connessioni al server da tenere in cache” e impostarlo a 1.

Guide

Cicli in C# .NET

Nei linguaggi di programmazione, il ciclo è una struttura di controllo iterativa che determina l’esecuzione di una porzione di programma ripetuta per un certo numero di volte o fino al verificarsi di una determinata condizione.

Ad esempio, se volessimo sommare ad una variabile tutti i numeri da 1 a 10, con le conoscenze apprese fino ad ora potremmo solamente utilizzare la seguente scrittura:

int answer;
answer = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10;

Ma ciò che abbiamo appena scritto andrebbe bene solamente se avessimo un numero limitato di numeri.

Nel caso in cui i numeri da sommare andassero da 1 a 100 siamo costretti ad utilizzare un differente metodo di scrittura che fa riferimento ai cicli.

Ciclo For

In C# abbiamo fondamentalmente 3 tipi di cicli: il ciclo do… while; il cliclo while….; e per finire il ciclo For.

In questa prima lezione utilizzeremo proprio quest’ ultimo ciclo per sommare ad una singola variabile tutti i numeri da 1 a 100.

Create quindi un nuovo progetto facendo click su File> Nuovo progetto, Aggiungete al form un bottone, dopodiché entrate nella schermata relativa la suo codice.

menu_InsertSnippet

Per inserire velocemente un ciclo fate click destro in qualsiasi punto tra le parentesi graffe del codice del pulsante.

Dal menu che appare, fate clic su Inserisci frammento di codice:

Quando si clicca su Inserisci frammento di codice, viene visualizzato un elenco di elementi

menu_InsertSnippet2

Dopo aver cliccato su For verrà inserito in automatico il relativo segmento di codice.

Snippet_Code_For

Sembra tutto un po ‘complicato, quindi andremo ad esaminare ogni singola componente del ciclo:

For ()
{

}

Si inizia quindi con la parola For seguita da due parentesi tonde, seguite poi dalle parentesi graffe nelle quali verrà inserito il codice del ciclo.

Esaminando il codice predefinito inserito da VisualStudio potrete facilmente intuire che il codice fra parentesi è diviso in 3 sezioni per mezzo di altrettanti simboli [ ; ].

loopStep1

La prima sezione del ciclo For viene solitamente utilizzata per dichiarare ed inizializzare le variabili di controllo.

Nel relativo caso stiamo dichiarando la variabile intera i, alla quale viene assegnato il valore 0.

La variabile i può essere comunque dichiarata a fuori dal ciclo:

int i

for(i = 0; {

}

loopStep2

A seguito va solitamente inserita la condizione di fine ciclo: nel nostro caso il ciclo dovrà terminare quando la variabile di controllo i, raggiungerà il valore 101.

La terza ed ultima porzione del codice fra parentesi viene utilizzate per aggiornare la variabile di controllo. In questo caso la dicitura:

variable_name + +

rappresenta un modo semplice per scrivere:

variable_name variable_name + 1 =

Quindi, per ricapitolare, è necessario impostare un valore iniziale, quante volte è necessario ripetere il codice fra graffe, e come va aggiornata la variabile di controllo.

for (Start_Value; End_Value; Update_Expression)

Ora, per realizzare il nostro programma copiate ed incollate il segmento di codice riportato a seguito

Code_ForLoop

Per comprendere più facilmente cosa accade ad ogni esecuzione del ciclo analizziamo i primi passi di esecuzione del programma.

1^ esecuzione

First time round the loop

2^ esecuzione

Second time round the loop

3^ esecuzione

Third time round the loop

e per finire 4^ esecuzione

Fourth time round the loop

Come si può facilmente intuire il ciclo verrà eseguito finche i non raggiungerà il valore 101, quindi al termine del ciclo la variabile answer conterrà il risultato della somma dei primi 101 numeri.

Guide

Soluzione a 554 Message Not Allowed – Headers are not RFC Compliant

Spedisci mail dal tuo server e Yahoo ti causa problemi in ricezione? La casella di posta del mittente non riceve i messaggi inviati dal tuo sito web o script in php? Ecco come ho risolto il problema “554 Message not allowed – Headers are not RFC compliant” riportato in tutti i log dei messaggi inviati a caselle di posta Yahoo.it!

Come chiaramente riporta l’errore, risulta subito evidente che il problema è legato alla funzione in PHP che viene utilizzato per l’invio dei messaggi. In particolare l’attenzione deve essere concentrata sulle informazioni inviate negli Headers, bisogna sapere che alcuni di essi vengono impostati in modo del tutto automatico dalla funzione mail() di php e quindi, una dichiarazione duplicata di tali headers, fa nascere il problema! Probabilmente Yahoo e altri gestori di posta elettronica utilizzano dei sistemi di controllo secondo i quali questo messaggio con headers sballati era considerato frode, spam o semplicemente non conforme e veniva rimbalzato!

La porzione di codice che ho dovuto analizzare era la seguente, come potete vedere sono evidenziate due righe che rappresentano la causa del male! Erroneamente è stato impostato, nella stringa degli headers, sia il “From” che “Subject” ma la funzione mail() di php esegue in automatico tale operazione. Questo rende obbligatoria la modifica di questa porzione di codice, eliminando le righe interessate in modo opportuno!

$subject = “Comunicazione periodica”;
$header = “From: <$email_from>\r\n”; //<—— PROBLEMA 1
$header .= “Subject: “.$subject.”\r\n”; //<—— PROBLEMA 2
$currDate=date(“D, d M y H:i:s O”);//DATE_RFC822
$header .= “Date: “.$currDate.”\r\n”;
$header .= “MIME-Version: 1.0\r\n”;
$header .= “Content-Type: text/html; charset=\”iso-8859-1\”\r\n”;
$header .= “Content-Transfer-Encoding: 7bit\r\n”;

$subject = “Comunicazione periodica”;
$header = “From: <$email_from>\r\n”; //<—— PROBLEMA 1
$header .= “Subject: “.$subject.”\r\n”; //<—— PROBLEMA 2
$currDate=date(“D, d M y H:i:s O”);//DATE_RFC822
$header .= “Date: “.$currDate.”\r\n”;
$header .= “MIME-Version: 1.0\r\n”;
$header .= “Content-Type: text/html; charset=\”iso-8859-1\”\r\n”;
$header .= “Content-Transfer-Encoding: 7bit\r\n”;

Dopo la modifica
$subject = “Comunicazione periodica”;
$currDate=date(“D, d M y H:i:s O”); //DATE_RFC822
$header .= “Date: “.$currDate.”\r\n”;
$header .= “MIME-Version: 1.0\r\n”;
$header .= “Content-Type: text/html; charset=\”iso-8859-1\”\r\n”;
$header .= “Content-Transfer-Encoding: 7bit\r\n”;

$subject = “Comunicazione periodica”;
$currDate=date(“D, d M y H:i:s O”); //DATE_RFC822
$header .= “Date: “.$currDate.”\r\n”;
$header .= “MIME-Version: 1.0\r\n”;
$header .= “Content-Type: text/html; charset=\”iso-8859-1\”\r\n”;
$header .= “Content-Transfer-Encoding: 7bit\r\n”;

La modifica, nel mio caso e fino ad ora ha sortito un effetto positivo consentendo la ricezione di messaggi su caselle Yahoo. Sicuramente in futuro bisognerà adattare il server/codice ad ulteriori misure di sicurezza che verranno adottate dai gestori, ma fino ad allora non avrete problemi.

Guide

Recensione Proiettore Artlii 600 Lumen

Volete stupire un vostro amico con un regalo alternativo? Preferite vedere film collegando comodamente il vostro smartphone o pc al videoproiettore?
Allora il videoproiettore Artlii potrebbe fare al caso vostro.

artlii

Questo videoproiettore portatile di ultima generazione coniuga efficacemente tutti gli standard necessari ad un videoproiettore con un prezzo assai basso che lo rende decisamente appetibile in un mercato in rapida crescita. Insomma, ormai per acquistare un videoproiettore efficiente non è più necessario sborsare più di qualche decina di euro. Ovviamente come tutti i proiettori portatili, come Philips Picopix 2055, presenta dei limiti fisici come la distanza di proiezione in proporzione alla qualità dell’immagine, la luminosità e l’altoparlante incorporato.

Funzionalità
Nonostante un prezzo molto ridotto questo videoproiettore LCD (per maggiori informazioni sulla tecnologia LCD, leggere la guida Luminosità dei colori) risulta molto duttile. Delle dimensioni di una mano, il videoproiettore Artlii può essere trasportato agilmente ovunque e per iniziare la visione sono solamente necessari pochi secondi. Infatti, è possibile collegare direttamente il proprio smartphone al videoproiettore tramite porta USB(Per collegare iPhone, è necessario il “iPhone al cavo HDMI”. Per collegare il telefono Android, è necessario il cavo MHL). Tuttavia, come qualsiasi videoproiettore che si rispetti, sono disponibile porte HDMI per collegare l’Artlii a console Xbox/PS3/PS4. La risoluzione massima supportata è di 1920 x 1080 pixel.
Da sottolineare come le dimensioni ridotte di questo prodotto e una risoluzione nativa di 320 x 240 pixel impediscano una distanza di proiezione considerevole. Nominalmente, l’Artlii rimane performante con una distanza di proiezione tra i 0.8 e i 2 metri spaziando rispettivamente tra i 24” e i 60” dell’immagine proiettata. Consigliamo di non superare i 1.2 metri(35”) per mantenere sempre una buona qualità d’immagine.
Un’interessante caratteristica include la possibilità di caricare il videoproiettore sfruttando le batterie esterne del telefono. Utile soprattutto se si è all’aperto o lontani da una presa.
L’altoparlante incorporato permette di evitare l’uso di altoparlanti esterni. Tuttavia si consiglia l’acquisto separato di un sistema audio se si vuole godere appieno l’esperienza di questo videoproiettore. Inoltre, è possibile, viste le dimensioni e la duttilità, di collegare delle semplici cuffie.

Luminosità e qualità d’immagine
Per quanto riguarda la luminosità, i 600 lumen nominali dell’Artlii permettono una immagine viva e dettagliata a distanze ravvicinate. Se si vuole una immagine proiettata più grande, è assolutamente necessario trovarsi in un ambiente semi-oscurato. Stesso discorso per quanto riguarda i colori: il videoproiettore Artlii ha un rapporto di contrasto 1:800.
La durata della lampada a LED può facilmente superare le 30’000 ore, allungabili se viene effettuata la manutenzione di tanto in tanto. Per maggiori informazioni consultare la guida “Manutenzione del videoproiettore”.

Riepilogo
Poco rumoroso e affidabile, il videoproiettore Artlii si è dimostrato un buon supporto per chi vuole spendere poco ma avere a disposizione un dispositivo in grado di proiettare film e immagini. Consigliato per il gaming ma poco funzionale per presentazioni d’ufficio o per PPT, questo videoproiettore portatile risulta essere per rapporto qualità/prezzo, uno dei migliori della categoria.

Vantaggi
– Piccolo e poco rumoroso
– Buono per il gaming o home cinema
– Prezzo facilmente abbordabile
– Tecnologia LCD
Svamtaggi
– Distanza di proiezione limitata
– luminosità ridotta
– Non consigliato per presentazioni di lavoro

Guide

Come Creare un Servizio Windows in .Net

Questo articolo affronta la creazione di un servizio windows .net con tre approcci diversi. Il più comune è quello basato su un timer (timer-based service), che semplicemente invoca il thread in background periodicamente…

Esistono anche le altre due alternative, a single thread e multi thread. Mentre l’approccio a timer è il più semplice, quello a single thread e sopratutto quello multi thread offrono alcuni vantaggi aggiuntivi.

La scrittura di un servizio .net è molto semplice. Bisogna semplicemente creare un progetto usando il Windows Service template. Poi bisogna inizializzare il servizio nell’evento OnStart e gestire la terminazione nell’evento OnStop aggiunto dal modello di Visual Studio.

protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
}

protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary
// to stop your service.
}
La soluzione con il Timer

Naturalmente, non è molto difficile far richiamre le funzioni che effettuano il lavoro vero e proprio, bisogna solo scrivere poche righe di codice per gestire il timer che attiva le chiamate periodicamente.
L’approccio a timer è il metodo più comune ed è probabilmente il più semplice da scrivere e capire. Devi creare un timer sull’evento OnStart ed agganciare le funzioni che effettuano il lavoro al timer.
Ecco l’esempio della soluzione a Timer:

// declare class-level variable for the timer
private Timer serviceTimer;

protected override void OnStart(string[] args)
{
TimerCallback timerDelegate =
new TimerCallback(DoWork);

// create timer and attach our method delegate to it
serviceTimer =
new Timer(timerDelegate, null, 1000, _interval);
}
DoWork è il metodo che contiene il codice per fare quello che vuoi periodicamente, quando scatta il timer.
Il prossimo listato mostra l’implementazione del metodo DoWork che effettua semplicemente la scrittura nell’EventLog.

private void DoWork(object state)
{
if (_workStartTime != DateTime.MinValue)
{
// probably check how much time has elapsed since work
// started previously and log any warning
EventLog.WriteEntry(“Warning! Worker busy since ” +
_workStartTime.ToLongTimeString(),
System.Diagnostics.EventLogEntryType.Warning);
}
else
{
// set work start time
_workStartTime = DateTime.Now;

// Do some work
// Note: Exception handling is very important here
// if you don’t, the error will vanish along with your worker thread
try
{
EventLog.WriteEntry (“Timer Service Tick :” + DateTime.Now.ToString());
}
catch (System.Exception ex)
{
// replace this with some robust logging technique
EventLog.WriteEntry(“Error! ” + ex.Message,
System.Diagnostics.EventLogEntryType.Error);
}

// reset work start time
_workStartTime = DateTime.MinValue;
}
}

Per ogni evento del timer si controlla se il lavoro che si sta eseguendo deriva dal precedente evento. Questo viene testato settando la variabile “_workStartTime” a DateTime.Now quando la funzione DoWork parte. La variabile è resettata a DateTime.MinValue quando tutto il lavoro da eseguire è completato.
Se la funzione sta ancora girando ed non ha finito registriamo un warning.

Si noti la gestione degli errori nella funzione DoWork. Se non gestite le eccezzioni e ne capitasse una, non riuscireste mai a sapere che errore è accaduto ed il vostro worker thread si limiterebbe a morire. Il servizio continuerebbe a funzionare normalmente senza sapere che il thread è terminato.

Con poche righe di codice abbiamo realizzato quello che volevamo. Ma ci sono altri modi per fare la stessa cosa ma in modo più elegante, vediamo come.

Alternativa 1: Usiamo un Thread Separato

La prima volta che ho scritto un servizio, mi son chiesto se potevo fare qualcosa di simile al listato sotto piuttosto che prendermi la briga di aggiungere un timer.

Il servizio instoppabile

protected override void OnStart(string[] args)
{
while (true)
{
// do some work

// idle
Thread.Sleep(0, interval, 0)
}
}
Ma subito mi son reso conto che cosi facendo il servizio sembrava rimaner appeso. Quando facevo partire il servizio non ottienevo una risposta da windows fintantoche il servizio non si bloccava nell’evento OnStart e il sistema operativo segnalava un errore. Un altro problema stava nel fatto che cosi facendo non si poteva dire al servizio di arrestarsi, perchè girava sempre nell’evento OnStart!

Per risolvere allora possiamo fare qualcosa di simile (noi dobbiamo invocare il metodo worker di lavoro). Useremo un thread separato. Iniziamo dichiarando alcune variabili di classe.

// This is a flag to indicate the service status
private bool serviceStarted = false;

// the thread that will do the work
Thread workerThread;

Il listato sotto mostra l’esempio con il single thread nell’evento OnStart

protected override void OnStart(string[] args)
{
// Create worker thread; this will invoke the WorkerFunction
// when we start it.
// Since we use a separate worker thread, the main service
// thread will return quickly, telling Windows that service has started
ThreadStart st = new ThreadStart(WorkerFunction);
workerThread = new Thread(st);

// set flag to indicate worker thread is active
serviceStarted = true;

// start the thread
workerThread.Start();
}
Il codice precedente istanzia un thread separato e vi attacca la nostra funzione “WorkerFunction”, nella quale metteremo il codice per eseguire i nostri task. Poi fa partire il thread e fa terminare l’evento OnStart, cosi che Windows non pensi che il servizio sia rimasto appeso.

L’implementazione della funzione WorkerFunction con l’implementazione single-thread

///

/// This function will do all the work
/// Once it is done with its tasks, it will be suspended for some time;
/// it will continue to repeat this until the service is stopped
///

private void WorkerFunction()
{
// start an endless loop; loop will abort only when “serviceStarted”
// flag = false
while (serviceStarted)
{
// do something
// exception handling omitted here for simplicity
EventLog.WriteEntry(“Service working”,
System.Diagnostics.EventLogEntryType.Information);

// yield
if (serviceStarted)
{
Thread.Sleep(new TimeSpan(0, interval, 0);
}
}

// time to end the thread
Thread.CurrentThread.Abort();
}
Questa funzione girà in un loop senza fine fino a quando la variabile “serviceStarted” non diventa false. Il parametro viene settato nell’evento OnStop come potete vedere nel listato sotto.

Implementazione dell’evento OnStop con l’implementazione single-thread

protected override void OnStop()
{
// flag to tell the worker process to stop
serviceStarted = false;

// give it a little time to finish any pending work
workerThread.Join(new TimeSpan(0,2,0));
}
Notate che abbiamo lasciato un tempo ragionevole al thread che esegue il lavoro (2 minuti nel listato sopra) affinchè riesca a completare il suo task. Ma se non ci riuscisse in quel tempo, la clausola “workerThread.Join” lo fa terminare in qualunque caso.

Alternativa 2: Usiamo più Threads (Multiple Threads)

Questa tecnica è molto simile alla precedente con il single-thread, ma il concetto viene esteso usando più threads. Questo pattern può semplicemente rimpiazzare l’alternativa a single-thread, usando soltanto un thread nell’array. Ora vediamo il codice. Per prima cosa dichiariamo le variabili di classe.

Variabili di classe nell’approccio multithread

// array of worker threads
Thread[] workerThreads;

// the objects that do the actual work
Worker[] arrWorkers;

// number of threads; typically specified in config file
int numberOfThreads = 2;
Vediamo l’evento OnStart

protected override void OnStart(string[] args)
{
arrWorkers = new Worker[numberOfThreads];
workerThreads = new Thread[numberOfThreads];
for (int i =0; i < numberOfThreads; i++)
{
// create an object
arrWorkers[i] = new Worker(i+1, EventLog);

// set properties on the object
arrWorkers[i].ServiceStarted = true;

// create a thread and attach to the object
ThreadStart st = new ThreadStart(arrWorkers[i].ExecuteTask);
workerThreads[i] = new Thread(st);
}

// start the threads
for (int i = 0; i < numberOfThreads; i++) { workerThreads[i].Start(); } } Per prima cosa creiamo un array di Worker objects della dimensione che ci necessita. Il Worker è una classe separata che contiene un metodo chiamato ExecuteTask che fa tutto il lavoro. Poi andiamo a dichiarare un array di threads e ci attacchiamo un Worker object ad ogni thread, specificando ExecuteTask come metodo da chiamare. Infine, lanciamo tutti i threads. Dopo di che, il metodo OnStart termina e il controllo ritorna a Windows. Il metodo “ExecuteTask” è simile ai metodi worker dei precedenti esempi. Il metodo ExecuteTask (Worker) nell’approccio multithread public void ExecuteTask() { DateTime lastRunTime = DateTime.UtcNow; while (serviceStarted) { // check the current time against the last run plus interval if ( ((TimeSpan) (DateTime.UtcNow.Subtract(lastRunTime))).TotalSeconds >= _interval)
{
// if time to do something, do so
// exception handling omitted here for simplicity
_serviceEventLog.WriteEntry(
“Multithreaded Service working; id = ” + this._id.ToString(),
EventLogEntryType.Information);

// set new run time
lastRunTime = DateTime.UtcNow;
}

// yield
if (serviceStarted)
{
Thread.Sleep(new TimeSpan(0,0,15));
}
}

Thread.CurrentThread.Abort();
}
Notate che usiamo un piccolo intervallo per il metodo Thread.Sleep (15 secondi) per aumentare la reattività quando il servizio viene arrestato. Se non avessimo fatto cosi il servizio sarebbe rimasto bloccato fino alla terminazione del periodo _interval (oppure sarebbe semplicemente andato in time out se l’intervallo fosse stato troppo lungo).

L’evento OnStop è molto simile a quello trattato nell’approccio single-thread, ad eccezzione del fatto che qui dobbiamo notificare a tutti i thread lo stop.

Metodo OnStop per l’approccio multithread

protected override void OnStop()
{
for (int i = 0; i < numberOfThreads; i++)
{
// set flag to stop worker thread
arrWorkers[i].ServiceStarted = false;

// give it a little time to finish any pending work
workerThreads[i].Join(new TimeSpan(0,2,0));
}
}

Compariamo i vari approcci

Oltre che per l’implementazione i tre metodi differisco in alcuni aspetti:

Performance Non ci sembra essere molta differenza anche se l’approccio col timer richiede un paio di thread supplementari per il timer.
Implementazione (codifica) L’approccio timer è il più semplice, quello a single-thread è leggermente più complesso e l’approccio con multithread richiede più codice.
Possibilità di eseguire più thread Ciò è disponibile nell’approccio multithread. Il numero di thread può essere specificato nel file di config, cosi chè è molto semplice aumentare o diminuire i worker thread. E’ inoltre possibile realizzare più thread anche nell’approccio timer, ma con codice aggiuntivo.
Possibilità di settaggio dei ritardi per ogni thread Con l’approccio multithread è possibile specificare il ritardo per ogni thread nel file di configurazione. L’approccio con il timer non è cosi flessibile; il timer interviene ad orari specifici, e per ottere la stessa cosa bisogna fare qualche accrocchio.
Il ritardo di un thread disturba l’esecuzione degli altri thread Questo non può accadere nell’approccio multithread. Ogni thread è in esecuzione ed ignora gli altri thread. Invece nell’approccio con il timer, se intervalliamo più worker thread, quello con tempo di esecuzione più lungo determina quando l’esecuzione successiva sarà possibile.

E ‘anche possibile generare più servizi all’interno dello stesso processo del servizio principale. Il listato sotto mostra il metodo Main() che il modello dei servizi .net genera.

static void Main()
{
System.ServiceProcess.ServiceBase[] ServicesToRun;

// More than one user service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = new System.ServiceProcess.ServiceBase[]
// {new Service1(), new MySecondUserService()};
//
ServicesToRun = new System.ServiceProcess.ServiceBase[]
{ new Service1() };

System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
Non ho parlato di questa opzione in questo articolo perché abbiamo considerato la possibilità di creare più thread in background in un determinato servizio, piuttosto che avere più servizi in esecuzione nello stesso processo.

Conclusioni

Questo articolo ha spiegato nel dettaglio come creare un servizio in .net con tre differenti approcci. Metre l’approccio col timer è il più semplice, quello multithread è molto flessibile e può lavorare con uno o più thread. Quest’ultimo approccio permette anche di gestire diversi ritardi per ogni thread. Qualsiasi ritardo di esecuzione nel metodo worker di un thread non interferirà con l’esecuzione degli altri thread.