176 messaggi dal 04 giugno 2007
Contributi | Blog
Creare oggetti Thread come nel codice che suggerisci none e' una best practice.
Sono oggetti costosi da creare e distruggere (anche se meno dei processi) e una cattiva gestione e' prona all'introduzione di problemi di frammentazione della memoria.

L'idea di fondo e' corretta, ma e' caldamente consigliato usare i thread gestiti dal ThreadPool del CLR e invece di creare manualmente un oggetto thread al quale passare il delegate da eseguire, chiamare il metodo statico QueueUserWorkItem sulla classe ThreadPool e lasciare che sia la logica del runtime a gestire il lifetime degli oggetti thread, introducendone o ritirandone quando opportuno nel ciclo di vita dell'applicazione.

Saluti da Redmond

--Alessandro
Ciao,

intanto grazie mille per il tuo commento.

La ragione per cui negli esempi non ho utilizzato il ThreadPool risiede nel fatto che questo è già utilizzato dal runtime di ASP.NET per gestire le web request. Pertanto accodare task nel thread pool tende di fatto a limitare il numero di utenti che possono essere gestiti contemporaneamente.

Ovviamente la mia decisione è stata subordinata al fatto che lo scenario mostrato è una WebApp. In un contesto differente userei di certo ThreadPool o APM.

Tu ne consigli l'uso anche in ASP.NET?
176 messaggi dal 04 giugno 2007
Contributi | Blog
Cradle ha scritto:
Ciao,

<snip>

Tu ne consigli l'uso anche in ASP.NET?


In generale, si'.

La ragione e' che iniettare nuovi thread manualmente ha un effetto anche sulle performance del ThreadPool, perche' di fatto perturbi l'ambiente in cui i suoi thread vengono eseguiti.
Il punto di usare il threadpool e' che hai un'entita' centrale che monitora costantemente la creazione e distruzione di oggetti thread tenuti pronti all'azione. I parametri che regolano il suo funzionamento sono legati a quanto lavoro e' accodato e quanto la macchina e' carica.
Creare thread a mano ha come effetto, al limite, la riduzione di thread disponibili per eseguire richieste del threadpool, quindi di fatto rischi di ottenere lo stesso effetto che volevi evitare.

Tieni conto che con .NET 3.5 il numero massimo di thread contemporanei per processore e' salito per default a 250 (vedi http://www.bluebytesoftware.com/blog/2007/03/05/WhyTheCLR20SP1sThreadpoolDefaultMaxThreadCountWasIncreasedTo250CPU.aspx per saperne di piu')

Chiaramante, in un server a basso carico, non c'e' un effetto visibile dovuto ad una strategia o l'altra, ma se il carico sale, usare il threadpool come l'arbitro di tutte le azioni asincrone, se non sono commessi errori di sincronizzazione che introducono deadlocks, potenzialmente aumenta la scalabilita' del sistema.

Saluti

--alessandro
Ciao Alessandro,
mi permetto di fare una riflessione sulle esecuzioni asincrone delle pagine ASP.NET.

Credo che spostare su un nuovo thread l'esecuzione di un'operazione non porti benefici, per le ragioni che hai detto te, ne tanto meno se lo si sposta su un altro thread del pool, perché sposteresti soltanto il problema, oltre ad avere un overhead di switch.

In ASP.NET gli handler IHttpAsyncHandler sono presenti non tanto per aumentare le performance, ma la scalabilità, così da sfruttare al massimo il server. Nel senso che, poiché anche IIS ha un suo thread pool di 100 (IIS6, non so se il 7 si integra con ASP.NET e quindi coincidono), nel momento in cui passa la palla ad ASP.NET, IIS è costretto ad impegnare quel thread per attendere una risposta, non accettando ulteriori richieste HTTP (ovviamente se è impegnato interamente).

Passando la palla ad un IHttpAsyncHandler, invece, il thread di IIS può liberarsi, ma per non spostare solamente il collo di bottiglia, occorre che anche l'handler invochi a sua volta in asincrono le operazioni su IO, DB e Socket. Questi tre elementi dispongono di metodi Begin/End e non sfruttanto il thread pool, ma le loro rispettive funzionalità. Solo così credo si ha un vero modello asincrono delle pagine.

Guardando gli assembly della Beta2 del .NET3.5 ho visto comunque che in modalità AutoConfig il limiti del thread pool sia IO e worker, sono rimasti a 100 per CPU come in ASP.NET 2. In console app o winform sono 250 come hai detto.

Spero di non aver detto castronerie, ma concordi con me?
Modificato da Ricciolo il 20 ottobre 2007 16.37 -

Ciao

Il mio blog
Homepage
176 messaggi dal 04 giugno 2007
Contributi | Blog
C'e' una parte del tuo commento che mi incuriosisce (piu' dal punto di vista terminologico che altro). Piu' che gestire operazioni in modo asincrono, mi sembra che la soluzione architetturale proposta nell'articolo verta sui benefici parallelizzazione delle computazioni pure. Per fare un esempio pratico, immaginiamo uno scenario in cui una moltiplicazione di matrici sparse possa essere spalmata su piu' thread. Patire tutte queste complicazioni semplicemente per avere il beneficio di un'operazione asincrona sicuramente non sembra una buona idea per tutte le ragioni che hai esposto.

Alla fine dei conti la risposta giusta e' sempre "Dipende".

Non e' difficile immaginare workloads in cui il flusso di web requests e' tale che spalmare l'esecuzione di una sola request su piu' thread (soprattutto se il server e' multicore/multiproc) puo' effettivamente migliorare le performance del sistema.
Per converso, e' facile immaginare situazioni in cui un'architettura del genere e' un suicidio.

I default del Threadpool sono controllati dall'Host del runtime. Se l'host non fa un override, nel 3.5 si parte con 250. Evidentemente l'host di ASP.net ha scelto di rimanere a 100 (presumibilmente per le ragioni che stiamo teorizzando - in generale il parallelismo computazionale in un applicazione web non paga).

Alla fine dei conti, il problema fondamentale e' che il Framework non ha (ancora) una buona libreria per rendere piu' accessibile il pattern fork-join di computazioni pure con bassa memory contention. Se ci fosse (gia') qualcosa del genere nelle librerie, programmare in modo piu' parallelo sarebbe piu' popolare.

Saluti

--Alessandro
Mi trovi d'accordissimo e credo che il parallel FX si stia muovendo in questo senso, anche se forse più per essere sfruttato da applicazioni client che server.
Non so, in un'applicazione web, se un'operazione richiede molta computazione nella maggior parte dei casi preferisco dare una risposta subito all'utente e processare con calma la richiesta con una coda. Se non attuassi questa politica sarei velocissimo a soddisfare alcuni, mentre neanche quasi accetterei le richieste di altri.

Comunque vorrei dirti grazie per la disponibilità che dai alla nostra community, è un onore averti tra noi

Ciao

Il mio blog
Homepage
Alessc-MSFT ha scritto:
...omissis...
Alla fine dei conti la risposta giusta e' sempre "Dipende".
...omissis...
Alla fine dei conti, il problema fondamentale e' che il Framework non ha (ancora) una buona libreria per rendere piu' accessibile il pattern fork-join di computazioni pure con bassa memory contention. Se ci fosse (gia') qualcosa del genere nelle librerie, programmare in modo piu' parallelo sarebbe piu' popolare.


Scusate se dico la mia solo ora, ho aspettato consapevolmente che la discussione evolvesse per capire meglio le argomentazioni degli uni e degli altri.

Mi sembra che alla fine dei conti la conclusione del discorso riportata sopra sia assolutamente condivisibile. Riprendendo peraltro le argomentazioni che hanno portato alla nascita di questa discussione, provo a dare la mia interpretazione dell'articolo, dal momento che l'ho discusso con l'autore, validato e pubblicato.

Fermo restando che l'uso del ThreadPool è consigliabile in molti scenari per le argomentazioni ben esposte da Alessandro, nel caso di task long running l'utilizzo del pool può avere un effetto controproducente. Se, per esempio, un servizio (nel caso dell'articolo si parla di servizi out-of-process) risponde con tempi dell'ordine di secondi o minuti, il rientro di un thread nel pool viene parecchio ritardato, il che introduce il problema di dover garantire il funzionamento dell'applicazione anche a fronte di una serie di richieste tali da saturare il pool stesso. Parallelamente l'uso del metodo SetMaxThreads per incrementare la dimensione del pool non lo vedo come una soluzione ottimale per risolvere il problema di scalabilità in questione, perchè sposta il problema semplicemente ad un nuovo limite.

Sebbene ASP.NET stesso proponga un meccanismo built-in basato sugli handler asincroni per migliorare la scalabilità proprio per evitare la starvation derivante dalla saturazione del pool (che in molti scenari web può rappresentare la soluzione da adottare), in questo caso abbiamo optato per scrivere un articolo sul multi-threading con soluzione "custom" semplicemente per cominciare a parlare dell'argomento.

Pertanto la soluzione proposta nell'articolo esce un po' dall'optimum avendo lo scopo di introdurre l'argomento del multi-tasking, argomento spesso sconosciuto e sottovalutato dagli sviluppatori web. In fase di revisione dell'articolo non nascondo che insieme all'autore abbiamo pensato all'eventualità di usare nell'esempio il ThreadPool, ma, data la finalità dello articolo, abbiamo optato per una soluzione più "comprensibile" per non mettere troppa carne sul fuoco.

Quindi per tirare le somme, dico che la soluzione ottimale DIPENDE dal tipo di applicazione e dal tipo di task da far eseguire ai thread. Questa discussione dimostra come la scelta di una soluzione rispetto ad un'altra può avere risvolti positivi o negativi a seconda dei casi e mette in evidenza alcuni dei limiti della versione attuale del .NET Framework per lo sviluppo di applicazioni che sfruttano il pattern fork-join, che spero vengano superati con le prossime uscite tra cui il Parallel FX.

Ringrazio Alessandro per l'attenzione rivolta alla nostra community e per i preziosi feedback.

Ciao, Ricky.

Ing. Riccardo Golia
Microsoft MVP ASP.NET/IIS
ASPItalia.com Content Manager
http://blogs.aspitalia.com/rickyvr
http://ricky.aspitalia.com
http://www.riccardogolia.it
Ricky, non stavo criticando l'articolo, anzi sono d'accordo con te che è l'occasione per parlare un po' di threding, argomento poco conosciuto.

Ciao

Il mio blog
Homepage

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.