Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere...

37
Scuola Politecnica e delle Scienze di Base Corso di Laurea in Ingegneria Informatica Elaborato finale in Programmazione I Gestione dei thread in C++11, C# e Java Anno Accademico 2015/2016 Candidato: Giuseppe Percuoco matr. N46002004 1

Transcript of Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere...

Page 1: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Scuola Politecnica e delle Scienze di BaseCorso di Laurea in Ingegneria Informatica

Elaborato finale in Programmazione I

Gestione dei thread in C++11, C# e Java

Anno Accademico 2015/2016

Candidato:

Giuseppe Percuoco

matr. N46002004

1

Page 2: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

[Dedica]

2

Page 3: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Indice

Indice .................................................................................................................................................IIIIntroduzione..........................................................................................................................................4Capitolo 1: Cosa è un Thread?.............................................................................................................6

1.1 Gestione dei thread.....................................................................................................................71.2 Concorrenza ...............................................................................................................................8

Capitolo 2: I tre linguaggi ..................................................................................................................10Capitolo 3: Thread in C++11..............................................................................................................13

3.1 Creazione e terminazione dei thread .......................................................................................133.2 Concorrenza..............................................................................................................................153.3 Concorrenza sui task.................................................................................................................17

Capitolo 4: Thread in C#....................................................................................................................194.1 Creazione e terminazione dei thread........................................................................................194.2 Concorrenza..............................................................................................................................214.3 Concorrenza sui task.................................................................................................................23

Capitolo 5: Thread in Java..................................................................................................................245.1 Creazione e terminazione dei thread .......................................................................................245.2 Concorrenza .............................................................................................................................26

Capitolo 6: Caso d'uso comune..........................................................................................................286.1 Implementazione nei tre linguaggi...........................................................................................28

Conclusioni.........................................................................................................................................32Bibliografia.........................................................................................................................................36

Page 4: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Introduzione

L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla

unione di ''info'' e ''matica'', ovvero ''informazione automatica '', da cui con il passare del

tempo ne è derivata una scienza che si occupa del trattamento e gestione dell'informazione

mediante procedure automatiche, intese come eseguibili da un calcolatore. Non molto

lontano, rispetto al concetto di informatica, è il concetto di ''algoritmo'' che formalizzando

una sua definizione può essere visto come un insieme di procedure le quali eseguite in un

certo ordine permettono la risoluzione di un determinato problema, basandosi sulle

informazioni necessarie caratterizzanti quest'ultimo. Dall'unione dell'informatica e del

concetto di algoritmo nasce il concetto di ''programma'' inteso come la codifica di un

qualsiasi algoritmo in un qualsiasi linguaggio di programmazione e che ne rende possibile

la sua esecuzione da parte di un calcolatore; viene da se che tale definizione è valida sotto

l'ipotesi che affinché un programmatore possa codificare un algoritmo in un qualsiasi

linguaggio di programmazione esso deve avere la peculiarità di essere risolvibile o in

gergo tecnico ''computazionalmente trattabile'' . Più in generale secondo una visione

statica, un programma non è altro che una descrizione delle elaborazioni da seguire.

Si suol dire « un programma è algoritmo più strutture dati », ma nel momento in cui tale

concetto viene dato in pasto ad un calcolatore che ne effettua una elaborazione, il concetto

statico di programma tende a svanire lasciando posto ad una entità dinamica chiamata

''processo''. Un processo è l'unità base di esecuzione che rappresenta l'attività dinamica di

un elaboratore in relazione ad una esecuzione di un programma, e come tale entità

dinamica esso varia nel tempo e per tale ragione viene identificato da: il programma

stesso, inteso come linee di codice, e da uno ''stato di esecuzione''. Oltre al concetto di

''esecuzione'', al processo viene associato anche il concetto di ''possesso e protezione delle

risorse'', definendo per ognuno di essi un proprio spazio di indirizzamento che rappresenta

l'insieme di indirizzi ai quali un processo può accedere.

4

Page 5: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Nella maggior parte dei casi dato un problema esso può essere risolto scomponendolo in

sottoproblemi di complessità minore ovvero, come nella realtà anche nell'informatica vale

il concetto di ''divide et impera''. Un problema risolto con tale metodologia prevederà alla

fine delle risoluzioni dei sottoproblemi una fase di combinazione degli stessi in un ordine

coerente. Dall'esigenza di svolgere tramite un calcolatore problemi sempre più complicati,

nasce il concetto di ''programmazione concorrente''. Essa viene intesa come un insieme di

specifiche tecniche, metodologie e strumenti necessari per agevolare l'esecuzione di

applicativi intesi come insieme di attività che vengono svolte simultaneamente. In

generale un processo non è una unità atomica ma può essere scomposto in una serie tali

attività , che nella maggior parte dei casi si identificano nel concetto di ''thread''.

5

Page 6: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Capitolo 1: Cosa è un Thread?

Un processo generalmente è costituito da un insieme di locazioni per memorizzare i dati,

un insieme di variabili globali e locali,da un suo relativo descrittore e da uno stack.

L'unione di queste informazioni sono definite come ''immagine del processo''. Ad ogni

processo viene definito un proprio spazio di indirizzamento, costituito principalmente

dalle risorse da esso possedute e dalla sua immagine.

Le operazioni di passaggio da un processo ad un altro (''context switch'') sono molto

onerose gravando principalmente sul tempo di esecuzione, con un conseguente aumento

dell' overhead dovuto al continuo succedersi di fasi di salvataggio e ripristino dello "spazio

di esecuzione ''. L'utilizzo di spazi di indirizzamento differenti per ogni processo, diviene

utile nel momento in cui ci interessiamo circa la protezione dei dati locali e ne favorisce la

sicurezza ad accessi non controllati in ambienti di scambio di messaggi tra processi, ma

tuttavia diventa complessa nel caso di interazioni mediante risorse comuni. Molti sono i

casi di applicazioni con un elevato grado di parallelismo e che condividono dati comuni (si

pensi ad applicativi in ambito di controlli reali di strutture fisiche , controllo di dispositivi

I/O, etc.).

La soluzione a tali problematiche è stata quella di introdurre nei moderni sistemi operativi

il concetto di ''processo leggero'', comunemente noto come ''thread''. Un thread è ''un

flusso di controllo sequenziale'' in un processo (''pesante''). Il perno su cui il thread poggia

le sue fondamenta concettuali è quello della separazione tra ''esecuzione'' e ''possesso di

risorse''. I due aspetti possono essere completamente separati dato che possono essere

gestiti dal SO in maniera indipendente. L'unità che viene eseguita è il thread, mentre il

possessore delle risorse è il processo o detto anche ''task''.

I threads appartenenti a uno specifico processo, condividono alle risorse, lo spazio di

indirizzamento e gli stessi dati di quest'ultimo. Ogni thread è associato uno stato di

6

Page 7: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

esecuzione, locazioni di memoria per le variabili ad esso locali, uno stack, un contesto

(insieme di valori presenti nei registi del processore in un dato istante) e un descrittore.

Da queste informazioni si traduce che a cause delle minime informazioni associate a un

thread, ne consegue che i cambi di contesto, creazione e terminazione risultano più

semplificate e veloci rispetto a quelle di un processo.

Definiamo ''multithreading'' la capacità di un sistema operativo di consentire l'esecuzione

di più thread in un processo.

1.1 Gestione dei thread

I thread possono essere gestiti sia a ''livello utente'' (user-level) che a ''livello

nucleo''(kernel-level).

In caso di prima gestione (Figura 1.1) si utilizzano librerie di funzioni (“thread package”)

implementata a livello utente, fornendo meccanismi di creazione, terminazione e

sincronizzazione dei thread. L'importanza di tale scelta è il sistema operativo ignora la

presenza dei thread, tendendo a gestire solo i processi. Nei casi generali un processo viene

inizializzato ad avere solo un thread che però ha la capacità di crearne altri tramite una

apposita funzione di libreria, così da creare una gerarchia di thread (padre-figlio) o livelli

di thread. Nel momento in cui un thread invoca una “system call”, il sistema operativo

blocca il processo a cui appartiene il thread e di conseguenza blocca anche i restanti

7

Figura 1.2Figura 1.1

Page 8: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

thread.

Nel secondo caso (Figura 1.2) la gestione a livello del nucleo di sistema (kernel-level)

impone che tutte le funzioni di realizzazione, terminazione, sincronizzazione etc., siano a

carico del sistema operativo e pertanto tali sono associate a chiamate di sistema.

1.2 Concorrenza

La ''Programmazione Concorrente'' è definita come l'insieme delle tecniche, metodologie

e strumenti per fornire supporto all'esecuzione di applicazioni, intese come insieme di

attività svolte simultaneamente. La ''Multiprogrammazione'' è la forma più elementare di

programmazione concorrente, e fornisce supporto alla esecuzione combinata di processi o

thread differenti. Essa permette di creare una macchina astratta che dispone di più

processori virtuali, uno per ogni processo, in modo da estendere le potenzialità fisiche

della macchina.

La ''Concorrenza'' è quel concetto per cui l'esecuzione di un insieme di processi si

sovrappone nel tempo. Gli strumenti che ci fornisce la programmazione concorrente sono

un insieme di primitive utili a definire : attività indipendenti (processi,thread),

comunicazione e sincronizzazione tra attività concorrenti (''concorrente'' non significa ''

parallelo'').

Le problematiche ricorrenti sono quelle relative a: comunicazione, condivisione di risorse,

assegnazione di risorse e sincronizzazione.

La ''Race Condition '' è quella situazione in cui più processi leggono e scrivono dati

condivisi, e il risultato finale è influenzato dall'ordine di esecuzione delle operazioni

all'interno dei rispettivi processi.

I processi concorrenti si dicono:

• Processi Indipendenti: P1 e P2 sono indipendenti se l’esecuzione di P1 non è

influenzata da P2, e viceversa.

• Processi Interagenti: P1 e P2 sono interagenti se l’esecuzione di P1 è influenzata da

8

Page 9: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

P2, e viceversa (dovuto alle differenti velocità di esecuzione dei processi).

I tipi di interazione sono:

• Competizione: r isorse comuni che non possono essere uti l izzate

contemporaneamente (“mutua esclusione”).

• Cooperazione: eseguire attività mediante scambio di messaggi (“comunicazione”).

• Interferenza: causata da competizione tra processi per l’uso non autorizzato di

risorse o ad una errata coordinazione di competizione e cooperazione.

In generale per un corretto funzionamento e avanzamento di processi concorrenti è

necessario imporre vincoli nella esecuzione delle operazioni, detti anche vincoli di

“Sincronizzazione”:

• Competizione: un solo processo alla volta deve avere accesso alla risorsa comune

(“sincronizzazione indiretta o implicita”).

• Cooperazione: le operazioni eseguite dai processi devono seguire una sequenza

prefissata (“sincronizzazione diretta o esplicita”).

La “risorsa” è un qualunque oggetto fisico/logico di cui un processo necessita per portare a

termine (o parzialmente a termine) le sue operazioni.

9

Page 10: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Capitolo 2: I tre linguaggi

C++11, analogamente noto come C++0x, è il nuovo standard per il linguaggio di

programmazione ISO C++ che di fatto sostituisce la passata versione risalente all'anno

2003. Il ''C++ Standard Committe '' ha completato il nuovo standard nel 2008, per poi

presentare una bozza nel 2010 e nel successivo 1° Settembre 2011 ufficializzare la

versione finale . Le esigenze di introdurre un nuovo standard erano quelle di migliorare la

velocità di programmazione, l'eleganza e la possibilità di agevolarene la manutenzione.

Si tratta del terzo standard ufficale per tale linguaggio : C++98 e C++03 non presentavano

modifiche rilevanti, mentre tale nuova versione aggiorna totalmente il linguaggio tanto che

lo stesso creatore Bjarne Stroustrup tende a definirlo come ''un nuovo linguaggio''. Le

principali novità introdotte riguardano:

• presenza di nuovi algoritmi

• nuove classi contenitore

• operazioni atomiche

• smart pointers

• funzione async()

• una librearia multithread

Il C# è un linguaggio di programmazione sviluppato da Microsoft durante l'iniziativa

.NET. Esso è orientato agli oggetti derivante principalmente da Delphi,C++, Java , Visual

Basic e pertanto offre all'utenza un linguaggio semplice e maneggevole rispetto agli stessi

C++ e Java. Le sue fondamenta si basano sulla piattaforma .NET Framework, che

oggigiorno è l'indiscussa tecnologia, appartenente allo Standard ISO, su cui Microsoft

poggia le sue fondamenta. Caratteristiche e componenti principali messi a disposizione dal

.Net Framework, sono:

• Common Languege Runtime (CLR): parte della tecnologia che si prende atto della

gestione dell'esecuzione delle applicazioni;

10

Page 11: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

• Common Language Specification (CLS): sezione che facilita l'interoperabilità tra

differenti linguaggi;

• Common TypeSystem (CTS): specifiche comuni per linguaggi differenti.

Il CLR ha il ruolo di eseguire le applicazioni .NET scritte in uno dei tipi di linguaggi che

la piattaforma supporta, il quale in una prima fase trasforma il codice in un una forma

ibrida comunemente chiamata IL (Intermediate Lenguage) . All'atto dell'esecuzione tale

codice viene assegnato ad un compilatore JIT-ter che lo converte in codice macchina e al

contempo attuando una ottimizzazione per il tipo di hardware su cui verrà eseguito.

Dall'altra parte il CLS e il CTS indicano una serie di regole che sia il compilatore che il

linguaggio devono sottostare affinché un componente possa interagire con altri

componenti scritti in linguaggi differenti e pertanto permettere la correttezza

dell'esecuzione dal CLR.

Java è stato creato da James Gosling presso la SunMicroSystem nel bel mezzo del

progetto “Green” nel 1991. Prima chiamato Oak (JDK 1.0), successivamente il nome Java

venne ufficializzato dalla Sun nel 1995. Esso è un linguaggio object oriented, indipendente

dall'architettura e multithread. Java dispone di un insieme di tool , racchiusi nel JDK, che

vengono a supporto duranti le fasi di sviluppo di un programma e nelle azioni necessarie

per la sua esecuzione. Tale linguaggio si basa sul principio << write once, run

anywhare>>, ovvero il codice una volta compilato non ha bisogno di una successiva

ricompilazione nel momento in cui viene lanciato su una differente piattaforma. Infatti una

volta scritto il codice in Java un apposito compilatore “javac” lo processa trasformandolo

11

Immagine 3.1

Page 12: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

non in un linguaggio macchina specifico della piattaforma su cui girerà, ma in un

linguaggio per un “processore virtuale” (JVM, Java Virtual Machine) detto “bytecode”.

In tale elaborato introdurremo prima il concetto di ''thread'', e in seguito tratteremo come

vengono implementati all'interno di tre linguaggi di programmazione: C++11,C# e Java.

12

Page 13: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Capitolo 3: Thread in C++11

L'ambiente di scrittura a cui facciamo riferimento per questo linguaggio e il classico “Dev-

C++”. Per una corretta compilazione è necessario usare il flag “-std=c++0x”.

Le librerie a cui faremo riferimento sono principalmente le classi presenti in : <thread>,

<mutex>, <condition_variable> e <future>.

3.1 Creazione e terminazione dei thread

La libreria messa a disposizione dal linguaggio è la libreria <thread>.Il costruttore del

thread (esempio in Figura 3.1.1) accetta come parametri di ingresso il task/funzione da

eseguire e i suoi parametri, ovviamente deve esserci una corrispondenza biunivoca gli

argomenti che la funzione ha in ingresso e quelli effettivamente passati.

13

Figura 3.1.1

Page 14: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Effettuata la ''costruzione'', il thread dichiarato inizia a eseguire non appena il sistema gli

affida le risorse necessarie. Gli argomenti utili alla esecuzione del task possono essere

passati sia per valore che per riferimento, in questo ultimo caso si utilizza la funzione

“ref()” dato che i costrutti dei thread sono “template variadici” e per questo necessitano di

un “reference_wrapper” per passare un riferimento. Opposto al costruttore c'è il distruttore

dell'oggetto thread il quale, nel caso in cui il thread è ''joinable'', chiama la funzione

“terminate()”. La funzione “join()” indica al main thread di non procedere fintanto che i/il

thread/s istanziati/o non abbiano completato, quindi rappresenta una sorta di punto di

sicnronizzazione fra il thrad appena istanziato e il “main thread”.

È possibile lasciare eseguire un thread al di fuori del proprio ambito invocando su di essi

la funzione “detach()”, così da lascarlo transitare in uno stato comunemente chiamato

“deamon”. Permettere a un thread di restare in esecuzione oltre il suo distruttore è

considerato dallo stesso ideatore del linguaggio un errore, dato che sarebbe più

professionale sempre accettarsi che un thread rilascia le risorse assegnatogli o che non

acceda a oggetti dell'ambito in cui è stato istanziato dopo che tale ambito sia stato

distrutto. Come mostrato in Figura 3.1.2 il thread “t1” una volta aver invocata la funzione

“detach”, lo stato di terminazione non viene rilevato dal main thread ed essendo

quest'ultimo più veloce di t1 termina in modo del tutto indipendente.

14

Figura 3.1.2

Page 15: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

3.2 Concorrenza

Il nuovo standard esprime molta importanza riguardo l'uso dei thread, incitando l'uso

cautelo degli stessi. Viene in primis sottolineato il concetto dell'evitare la “Data Race”.

Diremo che due thread hanno una data race se essi possono accedere a una posizione di

memoria in modo simultaneo e almeno uno dei due effettua una scrittura. Nel momento in

cui usiamo processi interattivi che agiscono su dati comuni, il linguaggio mette a

disposizione due forme di locking: “Mutex” e “Variabili Condition”.

Un mutex è un oggetto che viene realizzato per implementare l'accesso esclusivo a una

risorsa. Esso può essere posseduto da un unico thread per volta.

L'accesso al mutex viene effettuato con la funzione “lock()”, mentre il rilascio con la

funzione “unlock()” (come in Figura 3.2.1).

Un lock essendo una risorsa è necessario che una volta acquisita e consumata per

effettuare le proprie operazioni, debba essere rilasciata. La libreria standard mette a

disposizione due classi “lock_guard” e “unique_lock” che effettuano un “unlock()”

implicito (nella figura 3.2.2 per utilizzare un unique_lock basta sostituirlo a i lock_guard

presenti nel codice). La differenza tra lock_guard e unique_lock sta nel numero di funzioni

messe a disposizione, la prima è più semplice e veloce mentre la seconda seppur più

onerosa implementa funzionalità aggiuntive.

15

Immagine 3.2.1

Page 16: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

In genere può capitare di dover acquisire più risorse contemporaneamente; l'acquisizione

di più lock può, con molta probabilità, portare al deadlock (si avrebbe un deadlock se il

taskA effettuasse una lock su “a “mentre il taskB effettua una locksu “b”, Figura 3.2.3).

Le “variabili condition” sono implementate per effettuare una corretta comunicazione tra

thread, effettuando una sincronizzazione in relazione al verificarsi o meno di un evento.

16

Figura 3.2.3

Figura 3.2.2

Page 17: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

La figure 3.2.4 e 3.2.5 mostrano un piccolo esempio di utilizzo delle variabili condition e

delle funzioni “notify-wait”.

3.3 Concorrenza sui task

Per task si intende una attività che viene eseguita contemporaneamente da altre. Per thread

si intende la descrizione a livello di sistema delle risorse di un computer per l'esecuzione

di task. Un thread esegue un task. Lo standard offre supporto alla concorrenza per task che

17

Figura 3.2.4

Figura 3.2.5

Page 18: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

effettuano operazioni sui dati e produzione dei stessi. La comunicazione tra task avviene

mediante le funzioni “future/promise”. I task pongono il risultato nella “promise” (handle

di uno stato condiviso) attraverso una “set_value()” ( o “set_exception()” in caso di

propagazione dell'eccezione), e i task che necessitano del valore depositato potranno

ricavarlo dal relativo “future” attraverso una “get()”. Un “package_task” è un contenitore

di una coppia future/primise (Figura 3.2.6).

18

Figura 3.2.6

Page 19: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Capitolo 4: Thread in C#

L'ambiente usato per la stesura degli esempi in C# è “Visual Studio”. I namespace a cui

faremo riferimento per la trattazione dei thread

sono principalmente: “System.Threading” e

“System.Threading.Task”.

4.1 Creazione e terminazione dei thread

Nel linguaggio C# il codice viene gestito da un Manger thread, ovvero un thread che segue

politiche e comandi del CLR. Esso rappresenta una astrazione di più alto livello rispetto a

un qualsiasi thread di sistema.

All'interno del .NET Framework, un thread è rappresentato mediante la classe “Thread”

appartenente al namespace “System.Threading”. Il suo costruttore accetta un delegate, che

rappresenta la funzione da far eseguire al thread, di tipo “ThreadStart” o

“ParametrizedThreadStart” nel dobbiamo passargli anche alcuni parametri.

19

Figura 4.1.1

Page 20: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Distinguiamo il concetto di Thread di Foreground da quello di Background: il primo è in

grado di mantenere in vita l'applicazione fintantoché non è concluso, nel secondo caso

l'applicazione non deve aspettare il completamento del thread per poter terminare. Tale

proprietà è configurabile mediante la proprietà “IsBackground”. Il CLR mette a

disposizione un contenitore chiamato ThreadPool, in cui si mantiene una lista di thread

at t ivi . La classe s tat ica ThreadPool mette a disposizione un metodo

“QueueUserWorkItem”, che accetta un delegate di tipo “WaitCallback”, mediante il quale

possiamo accodare un nuovo task da eseguire in parallelo; per determinare/settare il

numero massimo di thread inseribili in un PoolThread, basta effettuare una chiamata al

metodo “GetMaxThread”/”SetMaxThread”.

Utilizziamo un oggetto di tipo Thread all'interno delle nostre applicazioni per la facilità di

gestire il flusso di esecuzione. Mediante l'esecuzione del metodo “Join” instauriamo una

sorta di sincronizzazione con il thread chiamante (inteso quello che chiama il metodo), che

consiste nell'aspettare la terminazione delle operazioni di quest'ultimo (Figura 4.1.2).

Un'altra possibilità è quella di interrompere completamente un thread, si pensi per esempio

di voler annullare l'esecuzione di una lunga serie di operazioni, e in questi casi il metodo

da utilizzare è “Abort” . Quando l'esecuzione del thread viene cancellata, il relativo

codice viene interrotto da una “ThreadAbortException” (Figura 4.1.3).

20

Figura 4.1.2

Page 21: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

4.2 Concorrenza

La prima tecnica che ci permette di sincronizzare gli accessi alle risorse comuni da parte

dei thread è l'uso della parola chiave “lock” (Figura 4.1.1). Essa definisce un blocco di

istruzioni, detto sezione critica, che deve essere sincronizzato cosicché un thread che inizi

ad eseguire tale sezione non possa essere interrotto. Il lock prevedete di accettare un

oggetto di tipo riferimento, che viene utilizzato come token da acquisire e da bloccare per

poter entrare nella sezione critica. Per problemi come l'incremento di una variabile, invece

di utilizzare l'istruzione lock, con la conseguente creazione di un oggetto, è possibile usare

la classe “Interlocked”.

L'istruzione vista in precedenza mediante la lock, viene interpretata dal compilatore

trasformandola in una classe “Monitor”. La classe Monitor prevede due metodi principali

che sono la “ Enter” e la “Exit” entrambe effettuate su un oggetto di tipo riferimento. Tale

classe, rispetto alla lock, permette di definire tramite l'istruzione “TryEnter” un tempo

massimo entro il quale un thread dovrà aspettare prima di entrare nella sezione critica.

Un'altra struttura che ci viene in aiuto per risolvere problemi di accessi concorrenti a

risorse comuni, è la classe “Semaphore”. La classe “Semaphore” limita il numero di thread

21

Figura 4.1.3

Page 22: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

che possono accedere ad una risorsa o ad un pool di risorse contemporaneamente. Il

costruttore della classe accetta come parametri di ingresso il numero massimo di accessi

iniziali e il numero massimo di possibili accessi contemporanei (Figura 4.2.4).

L'accesso alle risorse avviene invocando il metodo “WaitOne”, e il rilascio mediante il

metodo “Release”.

Un semaforo inizializzato per avere sono un permesso di accesso contemporaneo viene

detto Mutex. Per utilizzare il Mutex come controllore di accesso ad una sezione critica, si

utilizza il metodo “WaitOne”. Per rilasciarlo è necessario invocare “ReleaseMutex”

all'uscita del blocco condiviso (Figura 4.2.5).

22

Figura 4.2.4

Figura 4.2.5

Page 23: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

4.3 Concorrenza sui task

All'interno del .NET Framework troviamo una libreria chiamata Parallel Extensions,

formata da due componenti principali: Task Parallel Library (TPL) e Prallel LINQ.

La TPL contiene la classe “Task”, inserita nel namespace System.Threading.Task, e mette

a servizio del programmatore una serie di funzionalità per la creazione, controllo ed

esecuzione del codice parallelo. Esso fonda la sua interfaccia ( intesa come insieme di

funzionalità) sull'utilizzo di due tipi di delegate, “Action” e “Func”, a seconda dei casi se

eseguiamo una procedura o una funzione. La creazione di un task avviene mediante

l'oggetto “Task Factory”, accessibili mediante la proprietà “Task.Factory” e sfruttando il

metodo “StartNew”. Un task creato in tale modo viene subito schedulato ed avviato non

appena possibile. Quando creiamo un task passandogli un delegate di tipo Func, ovvero

una funzione, implicitamente andiamo a costruire un oggetto di tipo Task<Result>, cioè

una classe che deriva da Task e che espone la proprietà Result tramite la quale viene

recuperato il risultato dell'invocazione. Facendo così imponiamo una netta

sincronizzazione tra il task e il thread chiamante, il quale rimano bloccato fintantoché il

risultato non è disponibile.

Vengono esposti i principali due metodi che permettono la programmazione asincrona:

“async” e “awayt”. Anche all'interno dell'ambiente Task è possibile trovare una sorta di

gerarchia Padre-figlio chiamata “nested task”, in cui un task padre contiene un task figlio.

Nel momento in cui viene eseguito il task padre implicitamente viene eseguito anche il

figlio, il quale però per default ha un ciclo di vita indipendente. È possibile specificare, in

fase di costruzione la volontà di sincronizzare padre e figlio settando la proprietà

“TaskCreationOption.AttachedToParent”.

23

Page 24: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Capitolo 5: Thread in Java

L'IDE utilizzato è stato “ Eclipse Mars”.

I principali package utilizzati per la gestione dei

t h r e a d s o n o : “ j a v a . u t i l . c o n c u r r e n t ” ,

”java.util.concurrent.locks”

e “java.util.cuncurrent.Semaphore”.

5.1 Creazione e terminazione dei thread

Java supporta la programmazione multithread a livello di linguaggio, consentendo di

realizzare programmi multithread in maniera standardizzata e indipendente dalla

piattaforma su cui si esegue. Il linguaggio fornisce primitive per definire attività

indipendenti e primitive per la comunicazione e sincronizzazione di attività concorrenti.

La classe thread è presente nel package

“java.lang”. Java mette a disposizione due

possibili modalità di creazione di un thread:

• Derivazione della classe Thread;

• Implementazione dell'interfaccia

Runnable.

Perché java offre due distinti meccanismi

per la creazione di un Thread?

Semplicemente perché Java non consente la

derivazione multipla. Pertanto se una classe non è già coinvolta in un legame di

derivazione allora possiamo usare il primo metodo, altrimenti si usa il secondo metodo

ridefinendo la funzione “run()”.

Il costruttore principale della classe Thread é:

24

Immagine 5.1.1

Page 25: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

in cui gli passiamo il gruppo di appartenenza del thread, un oggetto eseguibile e il nome

del thread. All'atto della creazione, i thread possono essere raggruppati in un ThreadGroup

così da poterli controllare come se fossero una singola entità; ogni thread appartiene

sempre ad un gruppo, ma se non viene specificato nel costruttore si sottintende che quel

thread appartiene al gruppo di default chiamato “main”.

Introdotti nel package “java.util.concurrent”, i ThreadPool permettono di ridurre

l'overhead causato dalla creazione dei thread e permette di avere sotto controllo un numero

specificato di thread. Il tipo più comune di ThreadPool è il “newFixedThreadPool”, che

mantiene un numero fissi di thread in esecuzione e, nel momento in cui uno di essi viene

interrotto quando è ancora in uso, viene rimpiazzato automaticamente con un nuovo

thread.

Il metodo “start()” ha il principale obiettivo di allocare il rispettivo thread all'interno della

JVM e di invocare la funzione run(), la quale definisce il comportamento del thread.

Per quanto riguarda la fase di terminazione essa può avvenire in vari modi: mediante una

interruzione in cui un thread invoca il metodo “interrupt()” sull'oggetto thread da

interrompere sollevando una interruzione del tipo “InterruptedException”, mediante una

“suspend()” che sospende il thread ,che successivamente può essere riattivato mediante

una “resume”, oppure invocando il metodo “stop()” che blocca del tutto il thread e lo

“uccide”. In generale questi metodi sono “deprecati” in quanto l'uso degli stessi può

comportare notevoli complicazioni: si pensi ad un thread che venga interrotto prima di

rilasciare una risorsa, in tale modo si blocca completamente l'accesso alla risorsa a favore

di altri thread generando di conseguenza un deadlock difficilmente rilevabile e risolvibile.

Le funzioni più sicure per la sospensione e terminazione di un thread sono: la “sleep()”

che pone in uno stato di attesa/dormiente un thread per un numero di millisecondi

specificati, e il metodo “join()” che diviene utile nel momento in cui un thread padre

genera più thread figli e potrebbe essere necessario attendere la loro conclusione prima di

procedere.

25

Page 26: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

5.2 Concorrenza

Java fornisce un meccanismo di sincronizzazione basato su mutex per accedere alle

sezioni critiche. In generale ad ogni oggetto è associato un proprio mutex, il quale non

viene acceduto direttamente dall'applicazione ma solo attraverso l'uso di metodi/blocchi

sincronizzati. Nel momento in cui un thread esegue un blocco/metodo sincronizzato, se

esso è libero, entra in possesso del mutex associato all'istanza (“mutex lock”) garantendosi

un accesso esclusivo alla sezione, e eventuali thread che vogliano accedere alla risorsa

verrano posti in uno stato di attesa. Un metodo si definisce sincronizzato quando alla sua

firma viene anteposto la parola chiave

“synchronized”, e l'accesso al metodo è effettuato

solo nel momento in cui viene acquistato il lock

associato. Tale tecnica garantisce l'accesso in

mutua esclusione ai dati incapsulati in un oggetto solo se si accede per metodi definiti

sincronizzati. I metodi non sincronizzati possono essere eseguiti in ogni istante senza

proprietà di sicurezza relative alla mutua esclusione.

Java offre la possibilità di non dichiarare tutto il

metodo synchronized ma solo una parte di esso, in

questo caso la parola chiave accetta come parametro

un riferimento ad un oggetto del quale si vuole ottenere il lock.

Il vantaggio nell'usare metodi/blocchi synchronized sta nel fatto che permette al

programmatore di non avere la preoccupazione di rilasciare il mutex ogni volta che un

metodo termina normalmente o a causa di una eccezione , dato che viene eseguito

automaticamente.

Un'altra struttura che ci garantisce la concorrenza tra thread è il Monior. In Java un

Monitor viene realizzato mediante una classe che ha metodi synchronized e una variabile

condition. Una “variabile condition” definisce un meccanismo per sospendere thread in

attesa del verificarsi di una condizione. Un monitor è formato da due sezioni: una “entry

set” in cui sono racchiusi i thread pronti per accedere alla risorsa e una “wait set” in cui

26

Figura 5.2.2

Figura 5.2.3

Page 27: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

sono racchiusi i thread che sono stati sospesi o che sono in attesa del verificarsi di una

condizione. Le operazioni effettuabili sul monitor in Java sono : “wait()” in cui un thread

attivo nel monitor sospende la sua esecuzione con il conseguente passaggio nella wait set,

e rimarrà in quella regione fintantoché un altro thread attivo nel monitor non effettua la

“notify()”.

A partire da Java 1.5 sono stati introdotti nuovi costrutti per garantire e gestire una buona

sincronizzazione, tra cui spiccano classi come “Semaphore”, “Barriere” e “Lock”.

Il costruttore della classe Semaphore ha come parametri di ingresso il numero di permessi

da gestire per l'accesso alla risorsa comune ed un ulteriore parametro booleano che indica

la gestione ordinata (FIFO) o non dei thread che tentano l'accesso.

Le operazioni principali eseguibili su una oggetto Semaphore sono: “acquire()” che blocca

il thread corrente se non c'è almeno un permesso disponibile da acquisire, “release()” che

aggiunge un permesso al semaforo e potenzialmente permette di sbloccare un thread

bloccato in fase di accesso, e “tryAcquire()” che permette di acquisire un permesso sul

semaforo solo se è disponibile al momento della richiesta.

La classe “CyclicBarrier” permette ad un insieme di thread di aspettare ognuno il

raggiungimento da parte di tutti gli altri di un punto comune di sincronizzazione (barrieria)

oltre il quale riprendere l'esecuzione.

“Lock” è un'interfaccia del package “java.util.concurrent”, e le sue implementazioni

forniscono un uso più semplice rispetto a lock associati a blocchi/metodi synchronized.

Le funzionalità addizionali sono: la presenza di un tentativo non bloccante di acquisire il

lock (“tryLock()”), un tentativo di acquisire il lock interrompibile (“lockInterruptibly()”),

un tentativo di acquisire il lock che può essere interrotto da un timeout

“tryLock(long,TimeUnit)” o l'utilizzo di una classe chiamata “ReentrantLock(boolean

fair)” che permette di applicare politiche di fairness tra thread. I Lock vengono usati anche

in combinazione con variabili condition. L'interfaccia Condition mette a disposizione un

mezzo di sospendere l'esecuzione di un thread (“await()”) fino a quando non verrà

27

Figura 5.2.4

Page 28: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

notificato (“signal()”) da un'altro thread che una certa “condizione di stato” è ora vera.

28

Page 29: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Capitolo 6: Caso d'uso comune

Produttore consumatore / modello ad ambiente locale

Uno dei problemi di cooperazione ricorrenti nel campo dell'informatica è quello di avere

due entità , una scrive su una data risorsa e l'altra ne legge il contenuto. Tale problema

prende il nome di “Produttore/Consumatore”. Per garantire una corretta riuscita delle varie

operazioni di lettura/scrittura fatte dalle relative entità, viene la necessità di inserire alcuni

vincoli:

• il produttore non può inserire un nuovo valore prima che il consumatore abbia

prelevato il precedente;

• il consumatore non può prelevare il messaggio se prima non è stato prodotto.

Anche se esiste un problema di mutua esclusione nell'utilizzo della risorsa comune , la

soluzione impone un netto ordinamento nelle operazioni dei rispettivi processi.

È necessario che produttori e consumatori si scambino segnali per indicare l'esecuzione

delle rispettive operazioni. Entrambi i processi devono aspettare l'arrivo dell'altro

processo.

6.1 Implementazione nei tre linguaggi

• C++11:

29

Figura 6.1.1

Page 30: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

• C#

30

Figura 6.1.2

Figura 6.1.3

Figura 6.1.4

Page 31: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

• Java

31

Figura 6.1.5

Figura 6.1.6

Figura 6.1.7 Figura 6.1.8

Page 32: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

32

Figura 6.1.9

Figura 6.1.10

Page 33: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Conclusioni

C++11 C# Java

Mutex Presente in <mutex.h> Presente nel namespaceSystem.Threading

Implementabile tramiteuna classe semaphorep r e s e n t e i njava.util.concurrent

Monitor Da implementare Presente nel namespaceSystem.Threading

Da Implementare

Semaphore Implementabile tramiteMutex e Var iabi l iCondition

Presente nel namespaceSystem.Threading

Presente nel packagejava.util.concurrent

ThreadPool Da implementare Presente nel namespaceSystem.Threading

Presente nel packagejava.util.concurrent

Barriere Da implementare Presente nel namespaceSystem.Threading

Presente nel packagejava.util.concurrent.CyclicBarrier

P r i m i t i v e d iterminazione/sospensione/sincronizzazione/riattivazione

Join(), distruttore dellac l a s s e ~thread(),terminate(),sleep(),yield()

A b o r t ( ) , j o i n ( ) ,suspend(), sleep(), resume(), yield()

Destroy(), interrupt(),j o i n ( ) , r e s u m e ( ) ,sleep(), stop(), yield()

Condition Variables P r e s e n t i i n<condition_variables>

Da implementare Presente nel packagejava.util.concurrent.Locks

Tabella 1

Come è possibile notare dalla Tabella 1, l'aggiornamento della libreria standard di C++ al

multithreading tende a fornire solo costrutti base rispetto a Java e C#. Da qui nasce il

problema riguardante la scelta se è meglio avere strutture funzionali, semplici o

complesse, già disponibili o fornire solo funzioni base tramite le quali ricavare quelle più

articolate. Nel primo caso abbiamo che ciò che andiamo ad agevolare è il “riuso del

codice” il quale permette un notevole risparmio in termini di scrittura di un programma,

facilitando il lavoro della programmazione e al tempo stesso migliorando la leggibilità del

codice divenuto più corto e sintetico. Nel secondo caso fornire costrutti base per poi

33

Page 34: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

ricavarne altri più complessi fornisce una certa libertà al programmatore su come

strutturare a suo vantaggio gli stessi, in ogni minimo dettaglio.

Ovviamente fornire più strutture vuol dire mettere a disposizione meccanismi che tentano

di prevenire mal funzionamenti, ma ciò non toglie che per evitare situazioni come

deadlock e starvation è necessario un uso ottimale delle tecniche di programmazione

concorrente da parte dello sviluppatore. Per situazioni di deadlock, in entrambi i linguaggi

è consigliato scandire un ordine preciso di acquisizione (“lock”) delle risorse che cambia

da caso a caso. Per la starvation invece si è soliti fare uso dell'ausilio del metodo “yield()”

che permette al sistema di cedere il possesso della CPU ad un altro thread eseguibile,

oppure in casi particolari il linguaggio Java mette a disposizione meccanismi di politiche

di trattamento equo dei thread (classe ReentrantLock).

Abbiamo visto che la concorrenza nasce nel momento in cui ci sono più attività che

competono per il possesso di una risorsa comune. Istanziare un gran numero di thread e

strutture dati comporta la necessità di avere gestori di memoria performanti. In Java e C#

tutto ciò avviene in maniera del tutto trasparente per il programmatore, in quanto ci sono

rispettivamente il Garbage Collector e CLR che effettuano tali operazioni. In C++11 ciò

che si utilizza è la così detta tecnica RAII, acronimo di “Resource Acquisition Is

Initialization”, che lega il ciclo di vita di una risorsa (memoria allocata, mutex, thread, file

aperto, etc.) alla durata di un oggetto. Utilizzare l'acquisizione di una risorsa mediante un

costruttore e il rilascio della stessa in un distruttore, consente di eliminare “operazioni new

nude” e “ operazioni di delete nude”, in modo tale da rendere il codice molto meno

propenso ad errori. La stessa classe “std::thread” utilizza il protocollo RAII in quanto

acquisiscono la risorsa nei loro costruttori, che lanciano eccezioni nel momento in cui

l'acquisizione/inizializzazione non va a buon fine, e la rilasciano nei loro distruttori, senza

necessitare di meccanismi forzati di pulizia della memoria.

I due grafici mostrano rispettivamente lo speedup e il tempo di esecuzione di un

programma che esegue l'algoritmo di “decomposizione LU”, un algoritmo usato in campo

algebrico per la risoluzione di sistemi di equazioni lineari tramite matrici, al variare del

34

Page 35: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

numero di thread adoperati.

• C++11: lo speedup è per lo più proporzionale al numero di thread usati, anche se è

rilevabile una piccola diminuzione a partire da circa 30 thread usati .

• C#: lo speedup assume un andamento non proporzionale all'aumentare dei thread ,

tendendosi ad assestare a partire da circa 35 thread usati.

• Java: inizialmente tendente ad essere proporzionale all'aumentare del numero di

thread usati, ma dopo aver raggiunto un suo picco di speedup a circa 36 thread

tende a diminuire

35

Grafico 1

0 10 20 30 40 500

5

10

15

20

25

30

35

C++11

C#

Java

Numero di Thread

Spe

edup

Grafico 2

0 10 20 30 40 500

20

40

60

80

100

120

140

160

C++11

C#

Java

Numero di thread

Te

mpo

di e

secu

zion

e (s

ec)

Page 36: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Dal grafico dei tempi di esecuzione notiamo che il C# è di gran lunga più lento rispetto a i

restanti linguaggi, con un andamento che tende migliorare all'aumentare del numero di

thread. Il C++11 inizialmente risulta essere più lento del Java, ma all'aumentare del

numero di thread il loro tempi tendono a coincidere.

Le statistiche appena riportate sono del tutto generali, dato che inquadrano solo un tipo di

ambito d'uso.

Il C#, come il Java, lascia meno possibilità e libertà al programmatore, però assicurando

vantaggi riguardanti sia la sicurezza sia riguardo la velocità di apprendimento e leggibilità

del codice. C# e Java partono da un livello di astrazione superiore a quello del C++, il che

comporta un totale disinteresse circa tutte le problematiche che possono insorgere quando

si programma a basso livello perdendo quindi proprietà di flessibilità e potenza espressiva.

Ovviamente tale target di scelte da parte di questi due linguaggi non sono del tutto casuali:

nel caso di C#/Java, i loro ambiti di utilizzo sono principalmente tutti quei campi che si

occupano di sviluppare applicazioni che devono interagire pesantemente col mondo della

rete, del web, dei database, delle applicazioni distribuite, etc., per cui si è deciso di

“semplificare” la vita del programmatore fornendogli un livello di astrazione più alto.

Il C++ è stato pensato invece per andare a coprire target quali applicazioni time, sistemi

embedded, etc., ovvero tutti quesi settori per cui è necessario e conveniente agire a

differenti livelli di astrazione.

Quindi la domanda da porti non è “quale de tre linguaggi è il migliore per la trattazione dei

thread?”, ma è buona prassi studiare il proprio caso d'uso e scegliere il miglior linguaggio

per il relativo ambito di sviluppo della propria applicazione. Quindi la miglior risposta

resta sempre “dipende”.

36

Page 37: Gestione dei thread in C++11, C# e Java - Corso di Laurea ... · L'Informatica nasce per risolvere problemi. La sua etimologia deriva principalmente dalla unione di ''info'' e ''matica'',

Bibliografia

[1] Bjarne Stroustrup, “C++, Linguaggio, libreria standard, principi di programmazione”,

Pearson, 2015;

[2] Daniele Bocchino, Cristian Civera, Marco De Sanctis, Alessio Leoncini, Marco

Leonicini, Stefano Mostarda, “C# 6 e Visual Studio 2015, Guida completa per lo

sviluppatore”, Hoepli, 2016;

[3] Paolo Ancilotti, Maurelio Boari, Anna Ciampolini, Giuseppe Lipari, “Sistemi

Operativi, Seconda edizione”, McGraw-Hill,2008;

[4] Oracle, https://docs.oracle.com/javase/7/docs/api/;

[5] cplusplus, http://www.cplusplus.com;

[6] cppreference, http://en.cppreference.com/;

[7] MSDN,https://msdn.microsoft.com/it-it;

[8] “A comparative analysis between parallel models in C/C++ and C#/Java “,

http://kth.diva-portal.org/smash/get/diva2:648395/FULLTEXT01.pdf , KTH Information

and Communication Technology;

[9] Wikipedia, https://en.wikipedia.org/wiki/Comparison_of_Java_and_C%2B%2B ;

[10] Bruce Eckel, “Thinking in Java”, Prentice Hall, 2006.