• Fuentes

 #448094  por overxfl0w13
 08 Jul 2014, 15:52
Buenas, el otro día hablando con Sanko salió el tema de la sincronización y por no explayarme mucho en el tema porque la verdad, es bastante amplio, le comenté una forma de sincronizar sistemas distribuidos usando relojes físicos, la forma más costosa de sinconizar relojes, ya sea por coste económico (veáse Cristian, sistema heterogéneo con un servidor cuyo reloj físico es muy preciso y por tanto más caro) o por tráfico en la red (Berkeley).

El algoritmo que le comenté a Sanko es Berkeley por su simplicidad y porque no se hacen suposiciones como que el Tºenvío = Tºrecepción o que el Tºprocesamiento es conocido (Cristian).

Normalmente para sincronizar este tipo de sistemas no es necesario conocer la "hora" real, y nos sirve con asignarle un valor lógico a cada evento que suceda en el sistema ( L(e1),L(e2),...,L(eN) ), así definiendo la relación "happens before" expresada : e1 -> e2 (e1 ocurre antes que e2) podemos sincronizar eventos mediante algoritmos como Lamport o relojes vectoriales, éstos últimos para solucionar la problemática de la implicación si L(e1) -> L(e2) entonces e1 ocurre antes que e2 (en Lamport esto no ocurre).

Para simular el comportamiento de un conjunto de equipos conectados en red, he utilizado java básico (relativo a concurrencia) y si el análisis en papel no engaña el código es Thread-Safe.
El proceso creo que está medianamente explicado junto con el código, si tenéis cualquier duda comentad. Para los quisquillosos, como veréis el reloj del cliente tras las sincronizaciones no se ajustan del todo, eso es debido a la adición de un retraso (+/- lo que ocurriría en la vida real con el paso de un tiempo mucho mayor), si queréis que los tiempos se ajusten sin retraso alguno modificad en Client.java la variable addDelay a false :).

[Berkeley]
[Client.java]
public class Client extends Thread
{
    private int clientID;
    private long clientTime;
    private SimulatorMonitor sm;
    private final boolean addDelay = true;
    public Client(int clientID,SimulatorMonitor sm){
        this.sm         = sm;
        this.clientID   = clientID;
        this.clientTime = System.nanoTime();
    }
    
    public void run(){
        while(true){ // Clientes permanentemente conectados;
                // Actualizar temp y añadir un retardo específico a cada thread, en este caso (tiempoactual+id (thread+1)*2)
                this.clientTime += (this.addDelay) ? (this.clientID+1)*2 : 0;
                System.out.println("Temporización (cliente " + clientID + ") : " + this.clientTime);
                this.sm.setDiffTimes(this.clientTime,this.clientID); // Setear las diferencias, si el servidor aun no ha configurado su tiempo, los clientes duermen //
                this.clientTime += this.sm.getSettingTime(this.clientID); // Actualizar reloj del cliente
                System.out.println("Temporización acordada (cliente " + clientID + ") : " + this.clientTime);
        }
    }
                 
}
[Server.java]
public class Server extends Thread
{
    private SimulatorMonitor sm;
    private final int sleepMSeconds;
    private long serverTime;
    
    public Server(SimulatorMonitor sm){
        this.sm = sm;
        this.sleepMSeconds = 10000; // 10s
        this.serverTime = System.nanoTime();
    }
    
    public void run(){
        while(true){
            try{
                Thread.sleep(this.sleepMSeconds); // Duerme 10s
                // Despierta //
                /** Configura la hora del servidor en SimulatorMonitor, avisando a los clientes
                 * de que ya está configurada, el servidor se pondrá a dormir **/
                System.out.println("Temporización (servidor) : " + this.serverTime);
                this.sm.setServerTime(this.serverTime);
                /** Una vez despierte, los clientes ya habrán configurado las diferencias en el array
                 * se debe calcular la media y configurar los ajustes **/
                this.sm.calcAvgAndSet();
                /** Ajustar la hora del servidor (horaServidor + media) **/
                this.serverTime += this.sm.getAverage();
                /** Imprimir la temporización acordada **/
                System.out.println("Temporización acordada (servidor) : " + this.serverTime);           
                /** Cuando el servidor finalize se debe restaurar el estado inicial **/
                this.sm.restartProcess();
            }catch(InterruptedException e){} 
        }
    }
}
[SimulatorMonitor.java]
// Java -> modelo de monitor Lampson-Redell, los hilos esperan en la cola de suspendidos hasta que son despertados y van a la cola de entrada del monitor //
public class SimulatorMonitor
{
    private long serverTime;
    private long[] diffTimes;
    private long sumDiffs;  // Se mantiene esta variable para evitar recorrer el array de diferencias al calcular la media
    private final int numClients = 3; // 3 threads cliente con i = {0,1,2}
    private int countClientsOpered;
    
    public SimulatorMonitor(){
        this.serverTime = 0;
        this.countClientsOpered = this.numClients;
        this.diffTimes = new long[this.numClients];
        this.sumDiffs = 0;
    }
    
    public synchronized void setServerTime(long serverTime){ 
        this.serverTime = serverTime; // Se setea el tiempo actual del servidor
        try{
            notifyAll();                  // Se avisa a todos los clientes que estén esperando en la cola para poner las diferencias
            wait();                       // El servidor se pone a esperar
        }catch(InterruptedException e){}
    }
    
    public synchronized void setDiffTimes(long time,int n){
        try{
            if(serverTime==0) wait();              // Si el servidor todavía no ha configurado el tiempo los hilos cliente esperan en la cola de espera monitor
            this.diffTimes[n] = (time-serverTime); // Setea el array de diferencias
            this.sumDiffs    += time;              // Sumar a la variable sumDiffs
            countClientsOpered--;                  // Se decrementa el contador de clientes que han operado 
            //System.out.println("El cliente " + n + " setea diffTimes[n] con : " + time + "-" + serverTime + " = " + (this.diffTimes[n]));
            if(countClientsOpered==0) notify();    // Si ya han operado todos los clientes, se despierta al servidor de la cola de espera
            wait();                                // Los clientes esperan hasta que el servidor setee los ajustes de tiempo en diffTime
        }catch(InterruptedException e){}
    }
    
    public synchronized void calcAvgAndSet(){
        long avg = (this.sumDiffs / (this.numClients+1));
        for(int i=0;i<this.numClients;i++) this.diffTimes[i] = ((-this.diffTimes[i]) + avg);
        notifyAll();
    }
    
    public synchronized long getSettingTime(int n){ return this.diffTimes[n]; }
    public synchronized long getAverage()         { return this.sumDiffs / (this.numClients+1); }
    public synchronized void restartProcess(){
        this.serverTime = 0;
        this.countClientsOpered = this.numClients;
        this.sumDiffs = 0;
    }    
    
}
[Simulation.java]
public class Simulation
{
    public static void main(String args[]){
        SimulatorMonitor sm = new SimulatorMonitor();
        // Crear un hilo servidor e iniciarlo //
        Server srv = new Server(sm);
        srv.start();
        Client clv[] = new Client[3];
        // Crear hilos clientes e iniciarlos //
        for(int i=0;i<3;i++){
            clv[i] = new Client(i,sm);
            clv[i].start();
        }
            
    }
}
Imagen

Un saludo :D
 #448172  por strup
 09 Jul 2014, 00:14
aunque no entiendo mucho el java y el tema este del berkeley, te felicito por el codigo, y el gran talento que tienes, un saludo crack
 #448255  por Blau
 09 Jul 2014, 10:16
Coincido con strup, ¡eres un máquina!
 #491904  por davinciomar
 28 May 2017, 00:24
Esto esta de lujo :beer: