24. • Principio:
• Non bloccare i Thread.
• Pattern:
• Operazioni I/O-bound: conviene
sempre effettuarle in asincrono.
• Operazioni CPU-bound: dipende
dal contesto e dal costo
(computazionale).
24
25. Primary Secondary Parent/ Return/
Model Progress Cancel Wait Timeout
Use Use Child Exception
Queue
CPU Sync I/O No No No No No No
WorkItem
CPU via
APM I/O No No No Si No Si
BeginInvoke
CPU via
EAP I/O Background No Alcuni Alcuni No No Si
Worker
Si, via
Sync I/O or
TAP CPU Si No Cooperative Si Si Si
FromAsync
Cancellation
Si, via
Timer CPU Sync I/O No No No Si No
Dispose
25
27. • The Task-based Asynchronous Pattern
• Asynchronous Programming in C# and
Visual Basic
• Diving deep with WinRT and await
• http://blogs.msdn.com/b/pfxteam
• http://blogs.msdn.com/b/dotnet/
• 101 Async Samples
27
Editor's Notes
Prima di andareavanticapiamomeglio come operanoi Worker Thread del ThreadPool. QuandovieneinvocatoilmetodoQueueUserWorkItemdellaclasseThreadPoolilrelativoWorkItemvienemesso in una coda di lavoroglobale.Ogni Worker Thread cerca di prelevare un item usando un algoritmo di tipo FIFO.Unavoltapreso in carico, ilWorkItem, vienesemplicementeeseguito.Quando un Worker Thread terminal’esecuzione del WorkItemverifica se ci sonoaltrielementinella coda, se ci sono continua a prelevare, mentre se non ci sonosiimposta in Sleep.Passato un certo lasso di tempo il Worker Thread sisveglia e si auto termina in modo da liberare le risorse.Datoche la coda come dice ilnomestesso è globalel’accessoavviene in manieraconcorrentequindic’è un elevatorischio di conflitto.Nellevecchieversioni del framework l’accesso a questacosa era veicolato da un lock, il quale diventava un collo di bottiglia in caso di un elevatolavoroparallelo.Per questomotivonellaversione 4.0 è statautilizzatauna lock-free collection chepermette di scalaremeglio. DEMO - ThreadPoolMaxActiveThreadPRATICA - CPUBoundAPMPRATICA - CPUBoundTAP
Quandousiamoi Task ilfunzionamentodiventa un po’ piùarticolato.Come nelcasodeiWorkItemquando un Task vieneschedulatovieneposizionatonella coda globale e prelevatodai Worker Threads.Fin qui niente di nuovo. Però se un Task in esecuzione genera altri Task, questivengonomessi in una coda locale al Worker Thread.Terminatoil Task in corsoil Worker Thread preleva un nuovo Task da processare, questavoltautilizzando un algoritmo di tipo LIFO, per motivi di ottimizzazione, perchè molto probabilmenteilnuovo Task dovràlavorare con idatiappenagenerati dal vecchio Task, quindiancorapresenti in cache.Datocheil Worker Thread è l’unico Thread in grado di accedereallatestadella coda locale in questocaso non avviene un accessoconcorrente.Quando la coda di un Worker Thread è vuotaquet’ultimoruba un Task dalla coda locale di un’altro Worker Thread utilizzando un algoritmooppostoovvero FIFO, in questocasovieneprelevato un elemento in coda e non dallatesta al fine di ridurre I conflitti e preservare la cache.Quando non ci sonopiù Tasks nelle code localii Worker Thread tornano a prelevaredalla coda globale.Da qui in poi tornatutto come prima, quindi se non c’èpiùlavoro da svolgerei Worker Threads vanno in Sleep per un certo lasso di tempo, dopo di che se continua a non essercilavorosi auto terminano.