Arquitecturas cliente/servidor Servidores y Clientes Sincronizados
Contenido Procesos Semáforos Sincronización Lectura y Escritura de Archivos Servidores Orientados a Conexión Servidores No Orientados a Conexión
Procesos Un programa en ejecución es un proceso. Un proceso es cualquier secuencia de operaciones que están ejecutándose en memoria acpva, realizando una o varias instrucciones sobre ciertos datos.
Los procesos pueden ser concurrentes o paralelos.
Programación concurrente Cada proceso representa un programa secuencial que ejecuta una serie de instrucciones. Al ejecutarse un programa secuencial, éste sigue un solo hilo de control (thread), esto es se inicia con una operación atómica (indivisible) del proceso y se mueve a través del proceso conforme las operaciones se van ejecutando.
Procesos concurrentes Procesos disjuntos Son disjuntos si se ejecutan en diferentes bloques de programa, sin posibilidad de accederse entre sí. Ambos se ejecutan simultáneamente y proceden concurrentemente hasta que alguno o los dos terminan.
Procesos cooperapvos Tienen la necesidad de comunicarse entre sí en la realización de una tarea, comparpendo recursos en común por lo que requieren alguna forma de sincronización
Programación paralela Un programa paralelo o distribuido es aquel formado por varios procesos secuenciales que se ejecutan en varios procesadores conectados entre sí. Si la red se forma por conexiones dentro de una sola computadora se considera un esquema de programación paralela. Si la red se forma por conexiones entre diferentes computadoras se considera un esquema de programación distribuida.
Organización de memoria En un sistema mulpproceso existen recursos considerados comunes o comparpdos por todos los procesadores, como son el caso de la memoria y los medios periféricos. La comunicación entre los procesos se clasifica en dos Ppos: Fuertemente acoplados Para el caso en que el grado de interacción es alto Débilmente acoplados Para el caso contrario
Comunicación entre procesos La manera como la memoria es reparpda entre los procesadores determina el MECANISMO DE COMUNICACIÓN a uplizar: Variables compar:das En caso de memoria comparpda y por lo general procesos fuertemente acoplados. Paso de mensajes En caso de memoria distribuida y por lo general procesos bajamente acoplados.
Memoria comparpda Un sistema mulpproceso con memoria comparpda es aquél que permite el acceso de cualquier procesador del sistema a cualquier localidad de memoria común. Cada dirección es única e idénpca para todo procesador.
La comunicación entre procesos se realiza por lectura y escritura de variables en la misma sección de memoria. Memoria Procesador 1 P1 P2 P3
Memoria comparpda Para asegurar la integridad de los datos en memoria comparpda son necesarios: Mecanismos para el soporte de comunicaciones Ambiente de programación que provea la planeación Localización Sincronización Coordinación de procesos.
Memoria distribuida Un sistema mulpprocesador de memoria distribuida es aquél en el que cada procesador upliza su propia memoria privada. La comunicación con otros procesadores mediante una red de interconexión.
Programación Distribuida La comunicación entre procesos se realiza mediante envío y recepción de datos. Este esquema se conoce como paso de mensajes.
Conceptos de programación concurrente y paralela EXCLUSION MUTUA Se refiere a la situación en que dos o más procesos comunicándose entre sí, mantengan la integridad de los datos que comparten. En el caso de variables comparpdas, es la serialización de las acciones de escritura o lectura sobre una variable. En el caso de paso por mensajes se refiere a que cada proceso es capaz de recibir un solo mensaje a la vez.
Conceptos CONDICIÓN DE COMPETENCIA Dada la situación de exclusión mutua en la comunicación entre procesos se hace necesario que los procesos compitan entre sí para tomar posesión de las variables comparpdas, o en caso de paso de mensajes que dos o más procesos compitan porque su mensaje sea recibido antes por un tercer proceso.
Conceptos JUSTICIA Se refiere al hecho de proporcionar la oportunidad a todos los procesos que ejecuten sus acpvidades sin que ninguno quede suspendido o bloqueado permanentemente. GRANULARIDAD Indica la canpdad de instrucciones a ejecutar que cada procesador realiza con respecto al Pempo que cada proceso tarda en comunicarse
Conceptos BALANCE DE TRABAJO Para lograr un trabajo eficiente y una distribución equitapva de procesador(es) es necesario aplicar técnicas de balance de trabajo para que se distribuyan lo más uniformemente posibles los procesadores. Un balance de trabajo óppmo manpene los procesadores ocupados, procurando que todos ellos terminen casi al mismo Pempo.
Conceptos TERMINACION Se refiere al hecho de asegurar que todos los procesos de un programa concurrente finalicen correctamente. Si al menos uno de los procesos no termina por alguna causa, el programa concurrente simplemente no termina con éxito.
Dead lock
Abrazo mortal (dead lock) Un programa concurrente se encuentra en abrazo mortal si todos sus procesos se encuentran bloqueados entre sí, es decir, ninguno puede llegar a terminar También existe otro problema llamado inanición, que es cuando un proceso no puede llegar a ejecutarse, en el ejemplo de los filósofos que nunca pueda comer ya que no puede obtener los dos tenedores necesarios.
Si se Pene programación paralela eso significa que se puede dividir el Pempo de procesamiento en el número de procesadores existentes? P0 P1 P2 P3 P0 P2 P1 P3 P0 P2 P1 P3 Divide y vencerás.
Ley de Amdahl Propone normalizar el Pempo que toma realizar la operación en un solo procesador al valor de 1. La fracción del cálculo que sólo se puede realizar secuencialmente será F, entonces la fracción paralelizable es1- F. Entonces, el incremento de velocidad máximo que puede obtenerse con P elementos de procesamiento está dado por la ecuación:
El caso ideal Como un ejemplo, si nuestra aplicación no Pene sección secuencial, entonces el incremento de velocidad máximo estará dado exactamente por el número de elementos de procesamiento:
Programación concurrente y semáforos en Java En Java es posible ejecutar tareas en paralelo, uplizando hebras de control (hilos, threads). Este modo de programación permite tener un espacio de memoria, código o recursos comparpdos. Tiene la ventaja que su ejecución resulta más económica que un proceso completo.
Semáforos Java cuenta con semáforos implícitos de la forma: Object mutex = new Object(); /* */ Synchonized (mutex){ /* */ } Que solo pueden ser uplizados para exclusión mutua. Solo una hebra de control puede ejecutarse en el bloque synchonized en un momento dado.
Ejemplo del uso de la palabra Synchonized import java.io.*; class Banco { public stapc void main ( String args[]) { try { // Declaramos los dos montones de billetes Contador co1 = new Contador (); Contador co2 = new Contador (); // Declaramos los dos cajeros Cajero c1 = new Cajero(co1); Cajero c2 = new Cajero(co2); // Se ponen a contar.. c1.start(); c2.start(); c1.join(); c2.join(); } catch ( ExcepPon e ){ e.printstacktrace(); } } }
Clase Contador: Cuenta billetes y almacena la suma class Contador { int numbilletes = 0 ; long suma = 0 ; final int TOTAL_BILLETES = 10000 ; final int VALOR_BILLETES = 200 ; void cuenta () { // A contar la suma de los billetes for ( numbilletes =0 ; numbilletes < TOTAL_BILLETES; numbilletes ++ ) { suma += VALOR_BILLETES ; // Billetes de 200 pesos Thread.yield(); } System.out.println ( numbilletes+ " suman : "+ suma + " pesos"); } }
Clase Cajero: Recibe cierta canpdad de class Cajero extends Thread { Contador contadorcajero ; billetes para contar Cajero ( Contador paramcontador ) { contadorcajero = paramcontador ; } public void run () { contadorcajero.cuenta(); } }
Resultado: 10000 suman 2000000 pesos 10000 suman 2000000 pesos Es correcto, dado que cada cajero Pene su canpdad de billetes para contar.
ComparPendo el recurso Ahora supongamos que los dos cajeros deben contar del mismo montón, o sea, lo comparten y por tanto, la suma de lo que haya contado cada uno debe ser el resultado total. Para ello, modificaremos el código añadiendo lo siguiente Declaramos los dos montones de billetes : Contador co1 = new Contador (); // Ahora sobra, Contador co2 = new Contador (); // Declaramos los dos cajeros y el mismo montón. Cajero c1 = new Cajero(co1); Cajero c2 = new Cajero(co1); Con este cambio obtenemos: 10000 suman: 2000200 pesos 10001 suman: 2000200 pesos
El resultado anterior es incorrecto Por tanto, debemos uplizar un mecanismo de sincronización que garanpce que cuando un cajero cuente un billete y lo sume, el otro no pueda intentar coger el mismo billete y sumarlo. La solución que ofrece Java para resolver este problema es de lo más simple y eficiente, uplizando la cláusula synchronized en la declaración del método donde se realiza la tarea "crípca". Por tanto, cambiaremos el método void cuenta() por: synchronized void cuenta ()
Si realizamos ese cambio, obtenemos el siguiente resultado: 10000 suman : 2000000 pesos 10000 suman : 4000000 pesos Esto ocurre porque no se inicializa la variable suma antes del ciclo que cuenta los billetes, por lo que el segundo cajero conpnúa la suma en donde la dejó el anterior.
Inicializando el contador dentro de cada Si modificamos el código e incluimos la inicialización, tendremos: void cuenta () { // Cada cajero cuenta lo suyo suma = 0 ; // A contar la suma de los billetes for ( numbilletes =0 ; numbilletes < TOTAL_BILLETES ;numbilletes ++ ) { suma += VALOR_BILLETES ; Thread.yield(); } } A parpr de este momento obtenemos el siguiente resultado esperado tal y como detallamos: 10000 suman : 2000000 pesos 10000 suman : 2000000 pesos
Otra forma de realizar la sincronización consiste en declarar el objeto comparpdo como sincronizado en vez del método que lo conpene. Se realiza entonces el siguiente cambio: public void run(){ contadorcajero.cuenta(); } por: public void run(){ synchronized (contadorcajero ) { contadorcajero.cuenta(); } }
Monitores El siguiente nivel dentro de la solución a los problemas de exclusión mutua y sincronización entre procesos concurrentes fue desarrollado por C.A.R. Hoare y P. Brinch Hansen. Ahora se considera como recurso comparpdo no únicamente las variables comparpdas, sino también a los procedimientos y funciones que actúan sobre las variables
Seudocódigo Notación propuesta por Hoare (Simula 67) monitorname: monitor begin procedure //procname { parametros formales } begin // cuerpo del procedimiento // otros procedimiento locales del monitor end.. //inicialización de los datos locales del monitor end //declaraciones de datos locales del monitor
Monitores Productor Monitor (recurso comparpdo) Consumidor
Ejemplo
:TubTest t:tuberia p:productor c:consumidor new Tuberia() new Productor(t) new Consumidor(t) estavacia == false siguiente++ estallena==false siguiente- - estavacia == false siguiente++ lanzar(c ) recoger(c ) lanzar(c ) start( ) run( ) sleep( ) start( ) run( ) sleep( )
:TubTest t:tuberia p:productor c:consumidor new Tuberia() new Productor(t) new Consumidor(t) (estavacia==true)? start( ) lanzar(c ) recoger(c ) wait() run( ) sleep( ) start( ) run( ) estavacia== false lanzar(c ) nopfy() estallena==false siguiente- - sleep( )
Lectura y Escritura de Archivos import java.io.bufferedinputstream; import java.io.bufferedoutputstream; import java.io.fileinputstream; import java.io.fileoutputstream; public class LecturaEscrituraArchivos { public stapc void main(string args[]){ copiaarchivo("c:/archivoentrada.txt", "c:/archivosalida.txt"); }
public stapc void copiaarchivo (String archivolectura, String archivoescritura){ try{ FileInputStream fileinput = new FileInputStream(archivoLectura); BufferedInputStream bufferedinput = new BufferedInputStream(fileInput); FileOutputStream fileoutput = new FileOutputStream(archivoEscritura); BufferedOutputStream bufferedoutput = new BufferedOutputStream(fileOutput); byte [] array = new byte [1]; int leidos= bufferedinput.read(array); while(leidos > 0){ bufferedoutput.write(array); leidos=bufferedinput.read(array); } bufferedinput.close(); bufferedoutput.close(); }catch(exceppon e){ e.printstacktrace(); } } }
RandomAccessFile Mediante los objetos de esta clase uplizamos archivos binarios mediante un acceso aleatorio, tanto para lectura como para escritura. Existe un índice que nos indica en qué posición del archivo nos encontramos, y con el que se puede trabajar para posicionarse en el archivo. RandomAccessFile(String nombre, String modo) nombre: cadena idenpficadora del archivo modo: si será de lectura y/o escritura
Ejemplo de algunos métodos de escritura La escritura del archivo se realiza con una función que depende el Ppo de datos que se desee escribir. void write( byte b[], int ini, int len ); Escribe len caracteres del vector b. void write( int i ); Escribe la parte baja de i (un byte) en el flujo. void writeboolean( boolean b ); Escribe el boolean b como un byte. void writebyte( int i ); Escribe i como un byte. void writebytes( String s ); Escribe la cadena s tratada como bytes, no caracteres. void writechar( int i ); Escribe i como 1 byte. void writechars( String s ); Escribe la cadena s. void writedouble( double d ); Convierte d a long y le escribe como 8 bytes. void writefloat( float f ); Convierte f a entero y le escribe como 4 bytes. void writeint( int i ); Escribe i como 4 bytes. void writelong( long v ); Escribe v como 8 bytes. void writeshort( int i ); Escribe i como 2 bytes. void writeutf( String s ); Escribe la cadenas UTF Para la lectura existen métodos análogos para leer cada uno de los Ppos de datos.