Programación D - Simultaneidad

La concurrencia es hacer que un programa se ejecute en varios subprocesos a la vez. Un ejemplo de un programa concurrente es un servidor web que responde a muchos clientes al mismo tiempo. La simultaneidad es fácil con el paso de mensajes, pero muy difícil de escribir si se basan en el intercambio de datos.

Los datos que se pasan entre hilos se denominan mensajes. Los mensajes pueden estar compuestos por cualquier tipo y cantidad de variables. Cada hilo tiene una identificación, que se utiliza para especificar los destinatarios de los mensajes. Cualquier hilo que inicie otro hilo se denomina propietario del nuevo hilo.

Iniciar subprocesos en D

La función spawn () toma un puntero como parámetro e inicia un nuevo hilo desde esa función. Cualquier operación que lleve a cabo esa función, incluidas otras funciones que pueda llamar, se ejecutará en el nuevo hilo. Tanto el propietario como el trabajador comienzan a ejecutarse por separado como si fueran programas independientes.

Ejemplo

import std.stdio; 
import std.stdio; 
import std.concurrency; 
import core.thread;
  
void worker(int a) { 
   foreach (i; 0 .. 4) { 
      Thread.sleep(1); 
      writeln("Worker Thread ",a + i); 
   } 
}

void main() { 
   foreach (i; 1 .. 4) { 
      Thread.sleep(2); 
      writeln("Main Thread ",i); 
      spawn(≈worker, i * 5); 
   }
   
   writeln("main is done.");  
}

Cuando se compila y ejecuta el código anterior, lee el archivo creado en la sección anterior y produce el siguiente resultado:

Main Thread 1 
Worker Thread 5 
Main Thread 2 
Worker Thread 6 
Worker Thread 10 
Main Thread 3 
main is done. 
Worker Thread 7 
Worker Thread 11 
Worker Thread 15 
Worker Thread 8 
Worker Thread 12 
Worker Thread 16 
Worker Thread 13
Worker Thread 17 
Worker Thread 18

Identificadores de hilo en D

La variable thisTid disponible globalmente en el nivel de módulo es siempre el id del hilo actual. También puede recibir el threadId cuando se llama a spawn. A continuación se muestra un ejemplo.

Ejemplo

import std.stdio; 
import std.concurrency;  

void printTid(string tag) { 
   writefln("%s: %s, address: %s", tag, thisTid, &thisTid); 
} 
 
void worker() { 
   printTid("Worker"); 
}
  
void main() { 
   Tid myWorker = spawn(&worker); 
   
   printTid("Owner "); 
   
   writeln(myWorker); 
}

Cuando se compila y ejecuta el código anterior, lee el archivo creado en la sección anterior y produce el siguiente resultado:

Owner : Tid(std.concurrency.MessageBox), address: 10C71A59C 
Worker: Tid(std.concurrency.MessageBox), address: 10C71A59C 
Tid(std.concurrency.MessageBox)

Mensaje pasando en D

La función send () envía mensajes y la función ReceiveOnly () espera un mensaje de un tipo en particular. Hay otras funciones llamadas prioritySend (), receive () y receiveTimeout (), que se explican más adelante.

El propietario del siguiente programa envía a su trabajador un mensaje de tipo int y espera un mensaje del trabajador de tipo double. Los hilos continúan enviando mensajes de un lado a otro hasta que el propietario envía un int negativo. A continuación se muestra un ejemplo.

Ejemplo

import std.stdio; 
import std.concurrency; 
import core.thread; 
import std.conv;  

void workerFunc(Tid tid) { 
   int value = 0;  
   while (value >= 0) { 
      value = receiveOnly!int(); 
      auto result = to!double(value) * 5; tid.send(result);
   }
} 
 
void main() { 
   Tid worker = spawn(&workerFunc,thisTid); 
    
   foreach (value; 5 .. 10) { 
      worker.send(value); 
      auto result = receiveOnly!double(); 
      writefln("sent: %s, received: %s", value, result); 
   }
   
   worker.send(-1); 
}

Cuando se compila y ejecuta el código anterior, lee el archivo creado en la sección anterior y produce el siguiente resultado:

sent: 5, received: 25 
sent: 6, received: 30 
sent: 7, received: 35 
sent: 8, received: 40 
sent: 9, received: 45

Mensaje que pasa con espera en D

A continuación se muestra un ejemplo simple con el mensaje que pasa con wait.

import std.stdio; 
import std.concurrency; 
import core.thread; 
import std.conv; 
 
void workerFunc(Tid tid) { 
   Thread.sleep(dur!("msecs")( 500 ),); 
   tid.send("hello"); 
}
  
void main() { 
   spawn(&workerFunc,thisTid);  
   writeln("Waiting for a message");  
   bool received = false;
   
   while (!received) { 
      received = receiveTimeout(dur!("msecs")( 100 ), (string message) { 
         writeln("received: ", message); 
      });

      if (!received) { 
         writeln("... no message yet"); 
      }
   } 
}

Cuando se compila y ejecuta el código anterior, lee el archivo creado en la sección anterior y produce el siguiente resultado:

Waiting for a message 
... no message yet 
... no message yet 
... no message yet 
... no message yet 
received: hello