Hilos

Programación concurrente o multiproceso.

Originalmente los primeros sistemas operativos, como MS-DOS se ejecutaban en un hardware monoprocesador y solo se ejecutaba un proceso a la vez.

La programación concurrente es la parte de la programación que se ocupa de crear programas que pueden tener varios procesos/hilos que colaboran para ejecutar un trabajo y aprovechar al máximo el rendimiento de sistemas multinúcleo. En el caso de la programación paralela un solo ordenador puede ejecutar varias tareas a la vez (lo que supone que tiene 2 o más núcleos).

Por otro lado se denomina programación concurrente a la capacidad de un núcleo de ejecutar dos o más tareas a la vez, normalmente repartiendo el tiempo de proceso entre las tareas.

Con la aparición de otros SO’s más avanzados  se comenzó a usar la programación concurrente, el SO se ocupa de repartir el tiempo de la CPU para simular que varios procesos se ejecutan simultaneamente.

Proceso

Es un archivo que está en ejecución y bajo el control del sistema operativo. Un proceso puede atravesar diversas etapas en su «ciclo de vida». Los estados en los que puede estar son:

  • En ejecución: está dentro del microprocesador.
  • Pausado/detenido/en espera: el proceso tiene que seguir en ejecución pero en ese momento el S.O tomó la decisión de dejar paso a otro.
  • Interrumpido: el proceso tiene que seguir en ejecución pero el usuario ha decidido interrumpir la ejecución.
  • Existen otros estados pero ya son muy dependientes del sistema operativo concreto.
  • Un programa es un elemento estático, en cambio un proceso es dinámico, tiene asociadas variables, pilas y registros que solo él puede acceder.
  • Un proceso NO puede acceder a la zona de memoria asignada a otros procesos.
  • La información de cada proceso se guarda en una zona llamada BCP, bloque de control de proceso, con la información necesaria para pasar a modo activo.

 

Procesos y hardware asociado

  • Un sistema monoprocesador dispone de una única CPU, un multiprocesador de más de una.
  • Los procesos concurrentes se pueden ejecutar a la vez de forma real o simulada.
  • En los sistemas multiprocesador disponemos de varios procesadores o de un multicore.

Sistemas multiprocesador fuertemente acoplados.

  • Los procesadores comparten la memoria mediante un bus. Pueden ser simétricos o asimétricos (master-slave)

Sistemas multiprocesador débilmente acoplados.

  • No comparten la memoria, un ejemplo sería una red de ordenadores.

Tipos de procesos

  • Hablamos de multiprogramación, cuando los procesos se ejecutan en un solo procesador
  • Con la aparición de ordenadores con multiples procesadores se desarrolla la programación en paralelo.
  • En sistemas distribuidos hablamos de programación distribuida.

Servicios

Un servicio es un proceso que no muestra ninguna ventana ni gráfico en pantalla porque no está pensado para que el usuario lo maneje directamente.

Habitualmente, un servicio es un programa que atiende a otro programa.

No tienen interfaz, se ejecutan en segundo plano.

Con la mejora de las comunicaciones, se desarrollan sistemas distribuidos donde varios ordenadores ejecutan parte de una aplicación.

Ejecución de un programa

Ejecución de un programa, proceso de único hilo

 

Estados de los procesos y su ciclo de vida.

Cuando se crea un hilo este pasa a estar en el estado READY, es decir preparado para ser ejecutado, pasa a una cola a la espera de la CPU.

Crear un proceso externo desde Java con ProcessBuilder

Ejecuta un proceso desde Java mediante ProcessBuilder

package uno;

//Programa que detecta si se está ejecutando el comando que le pasamos como argumento y de ser así lo termina.

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;

public class Uno {

    public static void main(String[] args) throws IOException, InterruptedException{

        try
            {
        ProcessBuilder pb = new ProcessBuilder("open","/Applications");
        pb.start();

            }catch(Exception e)
        {
            System.out.println("Exception " + e);
        }

        }

}

Ejecuta un proceso desde Java mediante ProcessBuilder y lee la salida

package uno;

//Programa que detecta si se está ejecutando el comando que le pasamos como argumento y de ser así lo termina.

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;

public class Uno {

    public static void main(String[] args) throws IOException, InterruptedException{

            // en linux
            final String commands[] = {"ls", "."}; // estos son los comandos a ejecutar, seria: 
                                                  // usuario@usuario-pc:~$ ls /

            Process process = new ProcessBuilder(commands).start(); // se crea el proceso
                                                                   // usando los comandos

            // ProcessBuilder.directory(new File("ruta")); donde ruta = la carpeta del ejecutable

            // Se lee la salida
            InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);

            String line;
            while ((line = br.readLine()) != null) {
              System.out.println(line);
            }

            // Esperamos que el proceso termine
            try {
              int exitValue = process.waitFor();
              System.out.println("\nCódigo de salida: "+ exitValue);
            } catch (InterruptedException e) {
              e.printStackTrace(System.err);
            }

        }

}

Procesos vs hilos

Un hilo es un concepto más avanzado que un proceso: al hablar de procesos cada uno tiene su propio espacio en memoria. Si abrimos 20 procesos cada uno de ellos consume 20x de memoria RAM. Un hilo es un proceso mucho más ligero, en el que el código y los datos se comparten de una forma distinta.

Un proceso no tiene acceso a los datos de otro procesos. Sin embargo un hilo sí accede a los datos de otro hilo. Esto complicará algunas cuestiones a la hora de programar.

Un Proceso esta compuesto por un hilo como mínimo, este ocupa un espacio de memoria, esta formado por el código del programas, por sus datos y los ficheros que utiliza. A su vez tiene unos valores en los registros de la CPU, unos valores en la pila y un contador de programa

El sistema operativo representa toda esta información en el PCB (process control block).

Cuando ejecutan varios hilos, estos ocupan la misma memoria virtual, de esta forma comparten código, datos y ficheros, pero ejecutaran diferentes lineas de código y accederán a diferentes porciones de la memoria y diferentes entradas, esto significa que necesita su propios valores para el contador de programa, registros y pila.

 

Ejemplos de programación concurrente.

Imprimimos números del 1 al 10 en 6 hilos diferentes, la salida es impredecible pues los hilos no siguen un orden determinado.

Ejemplo extendiendo la clase Thread

public class NumberThread extends Thread {

int num;

public NumberThread(int n){
num = n;
//setPriority(n);
}

Ejemplo extendiendo el Interface Runnable

public class NumberRunnable implements Runnable {
    int num;
    
    public NumberRunnable(int n){
        num = n;
    }
    
    public void run(){
        for (int k=0; k < 200; k++){
            System.out.print(num);
        }
    }
}

Ejecutar los ejemplos anteriores

public class Numbers {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
NumberThread n1, n2, n3, n4, n5, n6;

n1 = new NumberThread(1); n1.start();
n2 = new NumberThread(2); n2.start();
n3 = new NumberThread(3); n3.start();
n4 = new NumberThread(4); n4.start();
n5 = new NumberThread(5); n5.start();
n6 = new NumberThread(6); n6.start();

// Usando EL interface Runnable nos permite convertir en hilos
// clases que ya tenemos creadas

Thread nt1, nt2, nt3, nt4, nt5, nt6;

nt1 = new Thread(new NumberRunnable(71)); nt1.start();
nt2 = new Thread(new NumberRunnable(72)); nt2.start();
nt3 = new Thread(new NumberRunnable(73)); nt3.start();
nt4 = new Thread(new NumberRunnable(74)); nt4.start();
nt5 = new Thread(new NumberRunnable(75)); nt5.start();
nt6 = new Thread(new NumberRunnable(76)); nt6.start();
}

}

Estados de un proceso

 

 

 

Planificación de procesos de la CPU

Procesos e hilos

 

Clase Semaphore en Java