Programação D - Simultaneidade

A simultaneidade é fazer um programa rodar em vários threads ao mesmo tempo. Um exemplo de um programa simultâneo é um servidor da web respondendo a vários clientes ao mesmo tempo. A simultaneidade é fácil com a passagem de mensagens, mas muito difícil de escrever se forem baseadas no compartilhamento de dados.

Os dados transmitidos entre threads são chamados de mensagens. As mensagens podem ser compostas de qualquer tipo e qualquer número de variáveis. Cada tópico tem um id, que é usado para especificar os destinatários das mensagens. Qualquer thread que inicia outro thread é chamado de proprietário do novo thread.

Iniciando Threads em D

A função spawn () pega um ponteiro como parâmetro e inicia um novo thread a partir dessa função. Quaisquer operações realizadas por essa função, incluindo outras funções que ela pode chamar, seriam executadas no novo encadeamento. O proprietário e o trabalhador começam a executar separadamente, como se fossem programas independentes.

Exemplo

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 o código acima é compilado e executado, ele lê o arquivo criado na seção anterior e produz o seguinte 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 thread em D

A variável thisTid disponível globalmente no nível do módulo é sempre o id do segmento atual. Além disso, você pode receber o threadId quando o spawn for chamado. Um exemplo é mostrado abaixo.

Exemplo

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 o código acima é compilado e executado, ele lê o arquivo criado na seção anterior e produz o seguinte resultado -

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

Passagem de mensagens em D

A função send () envia mensagens e a função receiveOnly () espera por uma mensagem de um tipo específico. Existem outras funções chamadas prioritySend (), receive () e receiveTimeout (), que são explicadas posteriormente.

O proprietário no programa a seguir envia a seu trabalhador uma mensagem do tipo int e aguarda uma mensagem do trabalhador do tipo double. Os threads continuam enviando mensagens para frente e para trás até que o proprietário envie um int negativo. Um exemplo é mostrado abaixo.

Exemplo

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 o código acima é compilado e executado, ele lê o arquivo criado na seção anterior e produz o seguinte resultado -

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

Passagem de mensagem com espera em D

Um exemplo simples com a mensagem passando com espera é mostrado abaixo.

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 o código acima é compilado e executado, ele lê o arquivo criado na seção anterior e produz o seguinte resultado -

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