Ruby e i Thread



Ruby include un supporto nativo al threading sulla falsariga di quello presente nel linguaggio Java, che implementa il supporto alla programmazione concorrente direttamente nel linguaggio.

Il supporto ai thread in Ruby e' pero' il risultato dell'implementazione di questa tecnologia direttamente nell'interprete del linguaggio, senza passare dai thread del sistema operativo: questo tipo di soluzione porta vantaggi ma anche lati negativi.

Uno dei maggiori vantaggi consiste nella portabilita': non basandosi sui thread nativi del sistema operativo, il threading di ruby non e' soggetto a funzionamenti diversi in base al sistema operativo 'ospitante'.

D'altra parte, il principale svantaggio consiste nella relativa 'giovinezza' del linguaggio: il supporto per il threading e' ancora immaturo, e spesso comporta problemi di dead-lock e starvation.

Problemi citati a parte, l'utilizzo dei thread in ruby e' di una semplicita' disarmante.Per creare nuovi thread e' necessario richiamare il metodo new della classe Thread:

Thread.new( [arg]* ) { | args | block |}

Il metodi si aspetta un parametro (o gruppo di parametri) e il blocco di codice da eseguire.Ad esempio:

x = Thread.new { sleep .1; print "x"; print "y"; print "z"}y = Thread.new { print "1"; print "2"; print "3"}

Appena creato, il thread parte con l'esecuzione del blocco di codice specificato. La possibilita' di passare parametri e' basilare per fornire al thread dati sui quali operare.

Un esempio concreto dell'utilizzo del threading potrebbe essere un semplice Echo Server: il programma si mette in ascolto su una porta TPC e appena un client si collega gli restituisce la prima stringa inviata e chiude la connessione. Implementando questo programma con i thread, i server potra' accettare piu' connessioni contemporaneamente.

require "socket"

echo_server = TCPServer.new(1234)

while true socket = echo_server.accept Thread.start do puts "Connessione avvenutan" s = socket line = s.gets puts "Linea Ricevuta:" + line + "n" s.puts line + "n" s.close endend

Vediamo il codice nel dettaglio:

require "socket"

include la libreria socket, necessaria per la creazione del server TCP.

echo_server = TCPServer.new(1234)

Crea un'istanza della classe TCPServer, aprendo un socket in ascolto sulla porta 1234.

while true socket = echo_server.accept

Inizia un ciclo infinito e si mette in attesa di connessioni.

Thread.start do puts "Connessione avvenutan" s = socket line = s.gets puts "Linea Ricevuta:" + line + "n" s.puts line + "n" s.close endend

Questo e' il cuore del nostro programma: appena viene stabilita la connessione, viene avviato un nuovo Thread (la sintassi e' leggermente diversa dagli esempi precendenti, il tutto per rendere migliore la leggibilita' in quando il blocco di codice da eseguire nel thread e' piu' nutrito) nel quale viene attesa una stringa dal client e, una volta ricevuta, viene reinviata al client stesso e chiusa la connessione.

Tutto questo blocco di codice viene eseguito come un 'processo' separato: infatti, appena creato il thread, il ciclo ricomincia dall'inizio e il server si rimette in attesa di un'altra connessione, senza dover aspettare che il client invii la sua stringa.

L'esempio e' estremamente semplice, non e' ad oggetti e ignora completamente le eccezioni, ma spero permetta di capire a grandi linee il funzionamento del threading:nei miei prossimi interventi sull'argomento tratteremo appunto la gestione delle eccezioni, l'enumerazione e la manipolazione dei Thread.

Published: December 18 2005

  • category: