Programmation D - Concurrence

La concurrence permet à un programme de s'exécuter sur plusieurs threads à la fois. Un exemple de programme simultané est un serveur Web qui répond à plusieurs clients en même temps. La concurrence est facile avec le passage de messages, mais très difficile à écrire s'ils sont basés sur le partage de données.

Les données transmises entre les threads sont appelées messages. Les messages peuvent être composés de n'importe quel type et de n'importe quel nombre de variables. Chaque fil a un identifiant, qui est utilisé pour spécifier les destinataires des messages. Tout thread qui démarre un autre thread est appelé le propriétaire du nouveau thread.

Lancer des threads en D

La fonction spawn () prend un pointeur comme paramètre et démarre un nouveau thread à partir de cette fonction. Toutes les opérations effectuées par cette fonction, y compris les autres fonctions qu'elle peut appeler, seraient exécutées sur le nouveau thread. Le propriétaire et le travailleur commencent tous deux à s'exécuter séparément, comme s'il s'agissait de programmes indépendants.

Exemple

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

Lorsque le code ci-dessus est compilé et exécuté, il lit le fichier créé dans la section précédente et produit le résultat suivant -

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

Identificateurs de thread en D

La variable thisTid disponible globalement au niveau du module est toujours l'id du thread actuel. Vous pouvez également recevoir le threadId lorsque spawn est appelé. Un exemple est présenté ci-dessous.

Exemple

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

Lorsque le code ci-dessus est compilé et exécuté, il lit le fichier créé dans la section précédente et produit le résultat suivant -

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

Message passant en D

La fonction send () envoie des messages et la fonction receiveOnly () attend un message d'un type particulier. Il existe d'autres fonctions nommées prioritySend (), receive () et receiveTimeout (), qui seront expliquées plus loin.

Le propriétaire du programme suivant envoie à son worker un message de type int et attend un message du worker de type double. Les threads continuent d'envoyer des messages dans les deux sens jusqu'à ce que le propriétaire envoie un entier négatif. Un exemple est présenté ci-dessous.

Exemple

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

Lorsque le code ci-dessus est compilé et exécuté, il lit le fichier créé dans la section précédente et produit le résultat suivant -

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

Message passant avec attente en D

Un exemple simple avec le message passant avec wait est montré ci-dessous.

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

Lorsque le code ci-dessus est compilé et exécuté, il lit le fichier créé dans la section précédente et produit le résultat suivant -

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