Servidores Multiproceso PID=1 atiende A Tema 2: Aplicaciones Multihilo, Cliente Servidor PID=2 atiende B Web Server process_new_connection() Web_Server_Main () t=1. B puja { { Multiples Clientes Concurrente if ((pid = fork()) == 0) for (;;) { { child_main(slot); new_connection = accept (i, NULL, NULL); Process_new_connection(); } } ap_scoreboard_image>parent[slot].pid = pid; return 0; } 1 Porqué Multitarea (multiples hilos o procesos) PID=3 atiende .. Fork() Fork() t=1. A puja } 2 Creación de procesos int pid; if (pid= fork()) == -1) perror(“error”); else if (pid == 0) /*código del proc. hijo*/ else /* código del proc. padre */ Para poder cancelar tareas independientemente. Algunos problemas son intrinsecamente paralelos: Simuladores, Servidores, Recolectores,.. Para beneficiarse de hardware con multiples procesadores. El núcleo del SO. con la llamada fork(): 1. Busca una entrada libre en la tabla de procesos y reservar memoria. 2. Asigna un identificador único al pro. hijo. 3. Realiza las copias del contexto del proc. padre para el proc. hijo. 4. Copia las tablas de control de ficheros del proc. padre para el proc. hijo. 5. Retorna al proceso padre el pid del hijo y al hijo el valor cero. P_padre 3 P_hijo 4 Procesos pesados vs. ligeros (I) Procesos pesados vs. ligeros (II) Proceso pesado = proceso unix o win32 o .. Proceso ligero = hilo (thread). La compartición de una CPU entre multiples tareas de tal manera que “se minimice el tiempo requerido para cambiar entre tareas”. Un proceso pesado en un sistema operativo se representa por su código, datos en memoria, y el estado de los registros de la máquina PCB. Esto se consigue “compatiendo el máximo posible del entorno de ejecución del programa” entre las diferente tareas de tal manera que muy poca información de estado se necesita guardar y cargar cuando se cambie de tareas. Los hilos de un mismo proceso pesado COMPARTEN el espacio de memoria. Los hilo siempre existen dentro de un proceso pesado, lo necesitan. Para poder soportar procesos ligeros (hilos de control), se dispone de múltiples pilas, una por cada hilo creado. 5 Hilos en Java (I) Implementaciones de Hilos A parent process may spawn child processes. Una clase Thread gestiona un único hilo secuencial de control. Los hilos pueden crearse y destruirse dinámicamente. A process may spawn child threads a process parent process 6 main thread child thread 1 Thread child processes child thread 2 run() Los hilo siempre existen dentro de un proceso pesado, lo necesitan. Programas que implementan la gestión de hilos, p.ej. Java Virtual Machine. MyThread •Las aplicaciones multihilo se ejecutan dentro del proceso del programa. run() La clase Thread ejecuta instrucciones incluidas en su método run(). El código real ejecutado depende de la implementación dada para el método run() en una clase derivada. class MyThread extends Thread { public void run() { //...... } } También existen sistemas operativos que implementan en su kernel la gestión de hilos: Windows NT, y muchas variantes de Unix. • en este caso los hilos también están dentro de un proceso. 7 8 Ejemplo Hilos en Java (I) import SomeThread ; public class RunThreads { public static void main (String[] args) { SomeThread p1 = new SomeThread (1); p1.start (); Ejecución de Ejemplo public class SomeThread extends Thread { int myID ; SomeThread (int id) { this.myID = id; } public void run( ) { int i; for (i = 1; i < 11; i++) System.out.println ("Thread"+ myID + ": " + i); } } //end class SomeThread SomeThread p2 = new SomeThread (2); p2.start (); SomeThread p3 = new SomeThread (3); p3.start (); } }// end class RunThreads Una clase Thread tiene que implementar el metodo run(), Una clase Thread se inicia llamando al metodo start(). 9 Se puede implementar el método run() directamente desde el interfaz Runnable, y asociarle un Thread para su ejecución. Este manera de usar hilos es más versátil, ya que puede derivarse de otras clases. target run() MyRun run() Salida Ejecución RunThreads con sleep(10): Thread1: 1 Thread2: 1 Thread3: 1 Thread1: 2 Thread2: 2 Thread3: 2 Thread2: 3 Thread1: 3 Cada 10 seg. los 3 Thread3: 3 hilos escriben Thread2: 4 “concurrentemente”. Thread1: 4 Thread3: 4 Thread2: 5 Thread3: 5 Thread1: 5 Thread2: 6 Thread3: 6 Thread1: 6 Thread2: 7 Thread3: 7 Thread1: 7 Thread2: 8 10 Thread3: 8 Ejemplo Hilos en Java (II) Hilos en Java (II) Runnable Salida Ejecución RunThreads: Thread3: 1 Thread3: 2 Thread3: 3 Thread1: 1 Thread1: 2 Thread1: 3 Thread1: 4 Thread1: 5 Thread1: 6 Totalmente Thread1: 7 Thread1: 8 “Concurrente” Thread1: 9 Thread1: 10 (aunque la Thread3: 4 Thread3: 5 salida de linux Thread3: 6 vs. Window Thread3: 7 Thread3: 8 suele diferir) Thread3: 9 Thread3: 10 Thread2: 1 Thread2: 2 Thread2: 3 Thread2: 4 Thread2: 5 Thread2: 6 Thread2: 7 Thread2: 8 public class RunThreads2 { public static void main (String[] args) { Thread p1 = new Thread(new SomeThread2(1)); p1.start(); Thread Thread p2 = new Thread(new SomeThread2(2)); p2.start(); Public interface Runnable { public abstract void run(); } Thread p3 = new Thread(new SomeThread2(3)); p3.start(); } class SomeThread2 implements Runnable { int myID; SomeThread2(int id) { this.myID = id; } public void run() { int i; for (i = 1; i < 11; i++) System.out.println ("Thread"+myID + ": " + i); } } //end class SomeThread } class MyRun implements Runnable { public void run() { //..... } } Un constructor de clase Thread: public Thread(Runnable target) 11 12 ¿hilo finalizado? (I) Ciclo de vida de un hilo en Java public class RunThreads3 { public static void main (String[] args) { int originalThreadCount = Thread.activeCount( ); for (int i=0; i<10; i++) { Thread p = new Thread(new SomeThread3()); p.start(); } sleep(),join() start() while (Thread.activeCount() > originalThreadCount ){ // loop until all child threads have exited. } start() comienza el hilo, siempre llama a run() // todos los hilos finalizados, continua. } sleep(x) duerme el hilo un tiempo. } join() espera a que otro hilo finalice. 13 ¿hilo finalizado?(II): isAlive() 14 ¿hilo finalizado? (III): join() // Create and start a thread Thread thread = new MyThread(); thread.start(); // Create and start a thread Thread thread = new MyThread(); thread.start(); // Check if the thread has finished // in a non-blocking way while (thread.isAlive()) { // Thread has not finished } else { // Wait indefinitely for the thread to finish try { Bloqueante thread.join(); // otro hilo finalizado, continua. ……. } catch (InterruptedException e) { // Thread was interrupted // otro hilo finalizado, continua. } } 15 16 Race Condition en Threads Syncronize en Threads class SomeThread3 implements Runnable { static int count=0; class SomeThread3 implements Runnable { static int count=0; Instruction execution order: SomeThread3() { super(); } public void run() { update(); } static public void update( ){ int myCount = count; int second = (int)(Math.random( ) * 500); try { Thread.sleep(second); } catch (InterruptedException e) { } myCount++; count = myCount; System.out.println("count= "+count); • • • • Instruction execution order: SomeThread3() { super(); } public void run() { update(); } static public synchronized void update( ){ int myCount = count; int second = (int)(Math.random( ) * 500); try { Thread.sleep(second); } catch (InterruptedException e) { } myCount++; count = myCount; System.out.println("count="+count); } Hilo 1 llama update, Hilo 2 llama update, … . • Hilo 1: “count = 1” Time • Hilo 2: “count = 2” • Hilo 3: “count = 2” • Hilo 4: “count = 1” • Hilo 5: “count = 1” • … • . • . No DESEADO • Hilo 1 llama update, • Hilo 1: “count = 1” • Hilo 2 llama update, • • • • Time • • • Hilo 2: “count = 2” Hilo 3 llama update, Hilo 3: “count = 3” Hilo 4 llama update, Hilo 4: “count = 4” . . } } } 17 Multiples Clientes “Concurrentes” 18 Paradigma Cliente Servidor Multiples Cliente – Un Servidor Servidor sin conexión: Hilo 1 atiende A t=1. A puja 50E por BMW Hilo 2 atiende B H1 H2 Usa IPC sin conexión (p.ej., datagram socket) Las sesiones con clientes concurrentes se pueden intercalar. Hilo 3 atiende C H3 Servidor con conexión: t=1. B puja 60E por BMW eBay Server Usa IPC con conexión (p.ej. stream-mode socket ) Sesiones con clientes concurrentes solo pueden ser secuenciales. Concurrencia necesita servidor multitarea. t=1. C Lee Puja Actual de BMW. 19 20 Sesiones de clientes concurrentes con servidor sin conexión EchoServer1 client 1 Servidor con Sockets sin conexión public class EchoServer1 { public static void main(String[] args) { … // instantiates a datagram socket for both sending and receiving data MyServerDatagramSocket mySocket = new MyServerDatagramSocket(serverPort); while (true) { // forever loop DatagramMessage request = mySocket.receiveMessageAndSender(); String msg = request.getMessage( ); mySocket.sendMessage(request.getAddress( ), request.getPort( ), msg); } //end while } client2 message echo message echo message echo message echo message echo 21 Servidores Iterativos (socket con conexión) Sesiones cliente consecutivas. EchoServer2 Client 1 Bucle Bucle = Sesión Servidor cliente. public class MyServerDatagramSocket extends DatagramSocket { MyServerDatagramSocket(int portNo) throws SocketException{ super(portNo); } public void sendMessage(InetAddress receiverHost, int receiverPort, string message); public String receiveMessage( ); public DatagramMessage receiveMessageAndSender( ); } //end class 22 Servidor Iterativo (socket con conexión) public class EchoServer2 { public static void main(String[] args) { Client 2 Message 1a ServerSocket myConnectionSocket = new ServerSocket(serverPort); while (true) { // forever loop MyStreamSocket myDataSocket = new MyStreamSocket (myConnectionSocket.accept( )); boolean done = false; while (!done) { message = myDataSocket.receiveMessage( ); if ((message.trim()).equals (endMessage)) { myDataSocket.close( ); done = true; } //end if else { myDataSocket.sendMessage(message); } //end else } //end while !done } //end while forever Echo 1a Message 2a Message 1b Echo 1b Message . Echo 2a Message 2b Echo 2b 23 Bucle servidor. Bucle sesión cliente. 24 Servidor concurrente: sesiones concurrentes Sequence diagram – EchoServer3 EchoServer3 accept EchoServer3 client 1 message echo message echo client2 accept EchoClient 1 EchoClient2 EchoServer3 thread 1 EchoServer3 thread 2 message echo message echo message echo 25 26 Servidor MultiHilo Hilo Atiende un Cliente import java.io.*; import java.net.*; public class EchoServer3 { public static void main(String[] args) { int serverPort = 7; // default port String message; try { // instantiates a stream socket for accepting // connections ServerSocket myConnectionSocket = new ServerSocket(serverPort); while (true) { // forever loop // wait to accept a connection MyStreamSocket myDataSocket = new MyStreamSocket (myConnectionSocket.accept( )); // Start a thread to handle this client's sesson Thread theThread = new Thread(new EchoServerThread(myDataSocket)); theThread.start(); } //end while forever } // end try catch (Exception ex) { Cliente ex.printStackTrace( ); } // end catch } //end main } // end class import java.io.*; class EchoServerThread implements Runnable { static final String endMessage = "."; MyStreamSocket myDataSocket; EchoServerThread(MyStreamSocket myDataSocket) { this.myDataSocket = myDataSocket; } public void run( ) { boolean done = false; String message; try { while (!done) { message = myDataSocket.receiveMessage( ); if ((message.trim()).equals (endMessage)){ myDataSocket.close( ); done = true; } //end if else { myDataSocket.sendMessage(message); } //end else } //end while !done }// end try Cliente catch (Exception ex) { System.out.println("Exception " + ex); } // end catch } //end run } //end class Por cada cliente: •Nuevo thread, •Start thread. Hilo2 Hilo1 Server Thread new Thread() Thread.start() Server JVM PID3 27 •Constructor recibe un DataSocket. •El código en run() se ejecuta para cada petición. Hilo2 Hilo1 run() ServerThread Server JVM PID3 28