Procesos en paralelo, multihilo, simultaneos y otras yerbas

Editado 10.01.2019

Cada tanto aparecen preguntas sobre estos tópicos en foro. En algunos casos son preguntas validas sobre procesos en paralelo, pero en la mayoría de las ocasiones se trata de un mal diseño de funciones.
Generalmente es un intento de integrar dos o mas códigos que funcionan bien por separados, pero juntos tienen comportamientos inesperados.
Por ese motivo me decidí a escribir un modesto tutorial sobre el tema, utilizando dos funciones de lo mas sencillas.
Las funciones están basadas en los ejemplos blink y blinkWithoutDelay

Pero antes un break culinario. Prepararemos una exquisita receta: un huevo duro

Receta de principiante

1 tomar un huevo
2 ponerlo en un recipiente, con agua, sobre el fuego.
3 esperar 10 minutos, sin hacer nada.
4 sacar el huevo.

Receta de masterchef para preparar un huevo duro

1 ¿Estamos preparando un huevo duro en este momento?
2 si la respuesta es no
2.1 tomar un huevo
2,2 ponerlo en un recipiente con agua sobre el fuego.
3 si la respuesta a la pregunta 1 es si
3.1 ¿ya pasaron 10 minutos?
3.2 si la respuesta de la pregunta 3.1 es no: hacer otra cosa
3.2 si la respuesta de la pregunta 3.1 es si: sacar el huevo.

sin duda la segunda receta parece mas complicada, sobre todo porque al final tenemos un huevo duro, pero es lo que hacemos en la realidad, cuando cocinamos no nos quedamos diez minutos viendo como hierve el huevo, nos ponemos a hacer otra cosa en esos diez minutos. Por eso para un observador externo parece que un cocinero esta haciendo varias recetas al mismo tiempo. Sin embargo, si intentamos unir varias recetas del primer tipo , a comida se alarga en el tiempo y parecerá que el cocinero se la pasa esperando.

Ahora un ejemplo de Arduino
El objetivo del programa es encender dos leds, uno rojo y otro verde, a diferentes frecuencias.

Led rojo con delays parpadeando cada 500 ms

int ledRojo = 14;
void setup() {                
  pinMode(ledRojo, OUTPUT);     
}

void loop() {
LedRojo;
}

void LedRojo()
{
  digitalWrite(ledRojo, HIGH);   
  delay(500);               
  digitalWrite(ledRojo, LOW);    
  delay(500);              
}

El mismo caso, pero sin delay

const int ledPinRojo =  14;      // the number of the LED pin


int ledStateRojo = LOW;             // ledState used to set the LED
long previousMillisRojo = 0;        // will store last time LED was updated
long intervalRojo = 500;           // interval at which to blink (milliseconds)

void setup() {
   pinMode(ledPinRojo, OUTPUT);      
}

void loop()
{
LedRojo ;
}


void LedRojo()
{
  unsigned long currentMillis = millis();
   if(currentMillis - previousMillisRojo > intervalRojo) {
     previousMillisRojo = currentMillis;   
      if (ledStateRojo == LOW)
      ledStateRojo = HIGH;
       else
      ledStateRojo = LOW;
     digitalWrite(ledPinRojo, ledStateRojo);
  }
}

En ambos casos se comportan igual

ledrojo.gif

Las funciones para un led Verde son equivalentes (parpadeando a un segundo)

int ledVerde = 15;
void setup() {                
  pinMode(ledVerde, OUTPUT);     
}

void loop() {
LedVerde;
}

void LedVerde()
{
  digitalWrite(ledVerde, HIGH);   
  delay(1000);               
  digitalWrite(ledVerde, LOW);    
  delay(1000);              
}

Y su equivalente sin delay

const int ledPinVerde =  15;      // the number of the LED pin
int ledStateVerde = LOW;             // ledState used to set the LED
long previousMillisVerde = 0;        // will store last time LED was updated
long intervalVerde = 1000;           // interval at which to blink (milliseconds)

void setup() {
   pinMode(ledPinVerde, OUTPUT);      
}

void loop()
{
LedVerde ;
}


void LedVerde()
{
  unsigned long currentMillis = millis();
   if(currentMillis - previousMillisVerde > intervalVerde) {
     previousMillisVerde = currentMillis;   
      if (ledStateVerde == LOW)
      ledStateVerde = HIGH;
       else
      ledStateVerde = LOW;
     digitalWrite(ledPinVerde, ledStateVerde);
  }


}

En ambos casos el comportamiento final es el mismo.

ledverdeanimacion.gif

Hasta aquí parece una complicación sin sentido no utilizar delays , pero cuando se integra el codigo

con delays el programa tiene un comportamiento inesperado.

int ledRojo = 14;
int ledVerde = 15;

void setup() {                
  pinMode(ledRojo, OUTPUT);  
  pinMode(ledVerde, OUTPUT);
}

void loop() {
LedRojo;
LedVerde;
}

void LedRojo()
{
  digitalWrite(ledRojo, HIGH);   
  delay(500);               
  digitalWrite(ledRojo, LOW);    
  delay(500);              
}

void LedVerde()
{
  digitalWrite(ledVerde, HIGH);   
  delay(1000);               
  digitalWrite(ledVerde, LOW);    
  delay(1000);              
}

incorrecta.gif

El problema reside en las funciones, se completan antes de devolver el control a la función void loop, por lo tanto se ejecutan en serie, es decir una detrás de la otra.

Es en este momento cuando aparecen los post de mulhilo y procesos en paralelo.
Sin embargo el código integrado sin utilizar delays funciona correctamente

const int ledPinRojo =  14;      // the number of the LED pin


int ledStateRojo = LOW;             // ledState used to set the LED
long previousMillisRojo = 0;        // will store last time LED was updated
long intervalRojo = 500;           // interval at which to blink (milliseconds)

const int ledPinVerde =  15;      // the number of the LED pin
int ledStateVerde = LOW;             // ledState used to set the LED
long previousMillisVerde = 0;        // will store last time LED was updated
long intervalVerde = 1000;           // interval at which to blink (milliseconds)

void setup() {
   pinMode(ledPinRojo, OUTPUT); 
   pinMode(ledPinVerde, OUTPUT);   
}

void loop()
{
LedRojo ;
LedVerde ;
}


void LedRojo()
{
  unsigned long currentMillis = millis();
   if(currentMillis - previousMillisRojo > intervalRojo) {
     previousMillisRojo = currentMillis;   
      if (ledStateRojo == LOW)
      ledStateRojo = HIGH;
       else
      ledStateRojo = LOW;
     digitalWrite(ledPinRojo, ledStateRojo);
  }
}


void LedVerde()
{
  unsigned long currentMillis = millis();
   if(currentMillis - previousMillisVerde > intervalVerde) {
     previousMillisVerde = currentMillis;   
      if (ledStateVerde == LOW)
      ledStateVerde = HIGH;
       else
      ledStateVerde = LOW;
     digitalWrite(ledPinVerde, ledStateVerde);
  }


}

correcta.gif

Como conclusión podemos decir que en la mayoría de los casos no son necesarios procesos en paralelos y se debe evitar a toda costa utilizar delays y otras estructuras que detengan el programa en algún punto.

Saludos

Que buen aporte!! Gracias!!

gracias por la informacion !!

Como puedo hacer si necesito controlar también las cantidad de veces que cada led (rojo y verde) se encenderá y apagara?