Pasar al contenido principal

Tema 8 - Hebras en C/POSIX y Java

Versión para impresiónSend by email

POSIX es la API (Interfaz de Programación) estándar para Unix, luego para Linux, Mac OS X, Android... y se define para C, C++, Ada...

En ARQO nos centramos en la creación de procesos (fork). En LPRS nos centraremos en la creación de Hebras, menos pesadas y comúnmente usadas para programación concurrente.

Hebras en POSIX

Como ya sabemos (ver Tema 1), todas las hebras comparten espacio de memoria y son mucho más ligeras que un proceso ya que los cambios de contexto son más rápidos.

Al arrancar un proceso se carga una hebra inicial que invoca main(). Desde esta hebra se pueden crear más hebras llamando a pthread_create() donde cada hebra ejecuta un código (método).

Cualquier hebra puede esperar a que termine una de sus hebras hijas con pthread_join() o forzar la terminación de cualquiera de ellas con pthread_cancel(pthread_t hebra).

Métodos Importantes

  • int pthread_create ( pthread_t *thread , pthread_attr_t *attrib , void *(*rutina)(void *) , void *arg );
    • pthread_t *thread: parámetro hebra que contendrá la hebra
    • pthread_attr_t *attr: atributos de la hebra, normalmente NULL
    • void *(*rutina)(void *): método que realizará la hebra
    • void *arg: Argumentos que recibe el método
       
    • Devuelve 0 si todo ha ido bien
  • void pthread_exit ( void *valor_de_retorno );
    • void *valor_de_retorno: String con el mensaje de terminación de hebra
  • int pthread_cancel( pthread_t hebra );
    
    • pthread_t hebra: Hebra a destruir

  • int pthread_join( pthread_t hebra, void ** valor_de_retorno );
    • pthread_t hebra: Hebra a la que se espera terminar
    • void **valor_de_retorno: Variable en la que se guardará el estado de la hebra que termina
       

Ejemplo:

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

void *trabaja(void *mensaje) {
  printf("Soy una hebra a la que han pasado un argumento: %s\n", mensaje);
  sleep(5);
  pthread_exit("bien");
}

int main(void) {
  pthread_t hebra;
  void *estado;

  if(pthread_create(&hebra, NULL, trabaja, "hola") != 0) {
    fprintf(stderr, "Error al crear hebra\n");
    exit(1);
  }

  sleep(3)
  pthread_join(hebra, &estado);
  printf("Terminada hebra con resultado %s\n", estado);

  exit(0);
}

 

Reentrancia

Cuando una hebra ejecuta un método, puede ser interrumpida por otra que ejecute el mismo método. Si este método opera con datos globales puede fallar. Para evitar esto, los métodos se definen como reentrantes, añadiendo la cabecera:

#define _REENTRANT

 

Hebras en Java

En Java las hebras son objetos. Igual que en C, la ejecución de un programa de Java crea una hebra que llama al método main(). El programa termina cuando todas las hebras terminan o cuando cualquiera llama al método exit().

En Java existen dos formas para crear hebras, extendiendo la clase java.lang.Thread, o implementando la interfaz Runnable.

En cualquier caso, el código que ejecuta la hebra se define en el método run(), y una hebra ha de ser arrancada (en C según se crea se arranca), usando el método start(). Los métodos adicionales de una hebra se pueden llamar aun habiendo finalizado la hebra anteriormente.

Extensión de la clase Thread

La clase Thread implementa de por sí la interfaz Runnable. Como Java no permite herencia múltiple, una de las limitaciones de usar esta forma es que la clase de la hebra no podrá heredar de ninguna otra.

Los métodos de la clase son:

  • void start();
  • void run();
  • static native void sleep (long millis) throws InterruptedException;
  • void join (long millis) throws InterruptedException;
  • void join () throws InterruptedException;
  • boolean isAlive();
  • public void interrupt() throws SecurityException;

Ejemplo:

import java.util.*;

public class Trabaja extends Thread {
  String mDespedida = "erróneo";
  String mensaje;

  public Trabaja(String mensaje) {
    this.mensaje = mensaje;
  }

  public String despedida() {
    return mDespedida;
  }

  public void run(){
    try{
      System.out.println("Arrancada hebra con argumento "+ mensaje);
      Thread.sleep(4000);
    } catch(InterruptedException ie){}
    mDespedida = "correcto";
  }

}

____________________________________________________________________________________

import java.util.*;
public class crearHebra {

  public static void main(String[] args) {
    Trabaja trabajador = new Trabaja("bien");
    trabajador.start();
    try {
      Thread.sleep(2000);
      trabajador.join();
    } catch(InterruptedException ie){}
    System.out.println("Terminada hebra con resultado " + trabajador.despedida());
  }

}

 

Interfaz Runnable

El concepto es muy similar, pero ahora sí podremos heredar desde la clase de la hebra de otra clase.

Ejemplo:

import java.util.*;

public class Trabaja implements Runnable {
  String mDespedida = "erróneo";
  String mensaje;

  public Trabaja(String mensaje) {    // Constructor
    this.mensaje = mensaje;
  }

  public String despedida(){
    return mDespedida;
  }

  public void run() {
    try {
      System.out.println("Arrancada hebra con argumento "+ mensaje);
      Thread.sleep(4000);
    } catch(InterruptedException ie){}
    mDespedida = "correcto";
  }

}

____________________________________________________________________________________

import java.util.*;

public class crearHebra {

  public static void main(String[] args) {
    Trabaja trabajador = new Trabaja("bien");
    Thread trabajadorT = new Thread(trabajador);
    trabajadorT.start();
    try{
      Thread.sleep(2000);
      trabajadorT.join();
    } catch(InterruptedException ie){}
    System.out.println("Terminada hebra con resultado " + trabajador.despedida());
  }

}