Programmazione D - Concorrenza

La concorrenza fa eseguire un programma su più thread contemporaneamente. Un esempio di un programma simultaneo è un server Web che risponde a molti client contemporaneamente. La concorrenza è facile con il passaggio dei messaggi, ma molto difficile da scrivere se si basa sulla condivisione dei dati.

I dati passati tra i thread sono chiamati messaggi. I messaggi possono essere composti da qualsiasi tipo e qualsiasi numero di variabili. Ogni thread ha un ID, che viene utilizzato per specificare i destinatari dei messaggi. Qualsiasi thread che avvia un altro thread viene chiamato proprietario del nuovo thread.

Avvio di thread in D

La funzione spawn () accetta un puntatore come parametro e avvia un nuovo thread da quella funzione. Qualsiasi operazione eseguita da quella funzione, incluse altre funzioni che può chiamare, verrà eseguita sul nuovo thread. Il proprietario e il lavoratore iniziano entrambi a eseguire separatamente come se fossero programmi indipendenti.

Esempio

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.");  
}

Quando il codice sopra viene compilato ed eseguito, legge il file creato nella sezione precedente e produce il seguente risultato:

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

Identificatori di thread in D

La variabile thisTid disponibile globalmente a livello di modulo è sempre l'id del thread corrente. Inoltre puoi ricevere il threadId quando viene chiamato spawn. Di seguito è mostrato un esempio.

Esempio

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); 
}

Quando il codice sopra viene compilato ed eseguito, legge il file creato nella sezione precedente e produce il seguente risultato:

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

Messaggio di passaggio in D

La funzione send () invia messaggi e la funzione receiveOnly () attende un messaggio di un tipo particolare. Esistono altre funzioni chiamate prioritySend (), receive () e receiveTimeout (), che verranno spiegate in seguito.

Il proprietario nel seguente programma invia al suo worker un messaggio di tipo int e attende un messaggio dal worker di tipo double. I thread continuano a inviare messaggi avanti e indietro fino a quando il proprietario non invia un int negativo. Di seguito è mostrato un esempio.

Esempio

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); 
}

Quando il codice sopra viene compilato ed eseguito, legge il file creato nella sezione precedente e produce il seguente risultato:

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

Passaggio del messaggio con attesa in D

Di seguito è mostrato un semplice esempio con il passaggio del messaggio 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"); 
      }
   } 
}

Quando il codice sopra viene compilato ed eseguito, legge il file creato nella sezione precedente e produce il seguente risultato:

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