D Программирование - Параллелизм
Параллелизм заставляет программу работать одновременно в нескольких потоках. Примером параллельной программы является веб-сервер, отвечающий множеству клиентов одновременно. Параллелизм прост при передаче сообщений, но его очень сложно написать, если они основаны на совместном использовании данных.
Данные, передаваемые между потоками, называются сообщениями. Сообщения могут состоять из любого типа и любого количества переменных. У каждого потока есть идентификатор, который используется для указания получателей сообщений. Любой поток, который запускает другой поток, называется владельцем нового потока.
Запуск потоков в D
Функция spawn () принимает указатель в качестве параметра и запускает новый поток из этой функции. Любые операции, выполняемые этой функцией, включая другие функции, которые она может вызывать, будут выполняться в новом потоке. И владелец, и рабочий запускаются по отдельности, как если бы они были независимыми программами.
пример
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.");
}
Когда приведенный выше код компилируется и выполняется, он считывает файл, созданный в предыдущем разделе, и дает следующий результат:
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
Идентификаторы потоков в D
ThisTid переменная доступна во всем мире на уровне модуля всегда идентификатор текущего потока. Также вы можете получить threadId при вызове spawn. Пример показан ниже.
пример
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);
}
Когда приведенный выше код компилируется и выполняется, он считывает файл, созданный в предыдущем разделе, и дает следующий результат:
Owner : Tid(std.concurrency.MessageBox), address: 10C71A59C
Worker: Tid(std.concurrency.MessageBox), address: 10C71A59C
Tid(std.concurrency.MessageBox)
Сообщение передается в D
Функция send () отправляет сообщения, а функция receiveOnly () ожидает сообщения определенного типа. Есть и другие функции с именами prioritySend (), receive () и receiveTimeout (), которые будут объяснены позже.
Владелец в следующей программе отправляет своему исполнителю сообщение типа int и ожидает сообщения от рабочего типа double. Потоки продолжают отправлять сообщения туда и обратно, пока владелец не отправит отрицательное int. Пример показан ниже.
пример
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);
}
Когда приведенный выше код компилируется и выполняется, он считывает файл, созданный в предыдущем разделе, и дает следующий результат:
sent: 5, received: 25
sent: 6, received: 30
sent: 7, received: 35
sent: 8, received: 40
sent: 9, received: 45
Сообщение передается с ожиданием в D
Ниже показан простой пример передачи сообщения с ожиданием.
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");
}
}
}
Когда приведенный выше код компилируется и выполняется, он считывает файл, созданный в предыдущем разделе, и дает следующий результат:
Waiting for a message
... no message yet
... no message yet
... no message yet
... no message yet
received: hello