52用d编程并发
并行基于并发.
并行与并发的区别:
并行,是利用多核.而单核也可以并发(多线程),如服务端程序
.
并行,相互独立,不独立,则为漏洞.并发则可以依赖其他线程结果.
并行,由任务
封装.并发显式利用线程
.
并行,易用,只要是独立任务
就可正常工作.并发只在基于传递消息
时才容易.基于传统的共享锁,很难编写正确的并发程序.
D支持两种并发:传递消息
(本章)和共享数据
(下章).
上章的任务基于std.parallelism
自动启动的线程.
即使简单的++i;
,也可能在之中停止.因为其包含三步:读值,增加,写回
,线程可在步骤间任意停止.
消息
:线程间传递的数据叫消息
,可包含任意类型及任意数量的变量.
线标
:线程标识符.用于指定消息的接收者(接收线程).
所有者
:启动线程的线程为新线程的所有者.
工作者
:所有新线程启动的线程都叫工作者
.
import std.stdio;
import std.concurrency;
import core.thread;
void worker() {
foreach (i; 0 .. 5) {
Thread.sleep(500.msecs);
writeln(i, " (worker)");
}
}
void main() {
spawn(&worker);
foreach (i; 0 .. 5) {
Thread.sleep(300.msecs);
writeln(i, " (main)");
}
writeln("main is done.");
}//分开独立运行的线程
一旦启动新线程,两者就像独立程序一样分开执行,
spawn
启动,spawn
产生的线程可相互通信,而task
不能.
import std.stdio;
import std.concurrency;
import core.thread;
void worker(int firstNumber) {
foreach (i; 0 .. 4) {
Thread.sleep(500.msecs);
writeln(firstNumber + i);
}
}
void main() {
foreach (i; 1 .. 3) {
spawn(&worker, i * 10);
}
}
传递参数.
每个操作系统同一时间,都会限制线程数.可对单独用户或整个系统或其他设限.如果有比核数还多的线程在忙,整个系统性能就差了.基本上是密集运行的线程,叫cpu密集
线程.花大量时间等待用户输入,网络数据,完成Thread.sleep
调用等的线程,叫IO密集
线程.
如果是io密集
线程,就可启动新线程,而不损伤性能.因而必须根据实际情况仔细设计.
import std.stdio;
import std.concurrency;
void printTid(string tag) {
writefln("%s: %s", tag, thisTid);//线标.无().
}
void worker() {
printTid("Worker");
}
void main() {
spawn(&worker);
printTid("Owner ");
}
线标不重要.
Owner : Tid(std.concurrency.MessageBox)
Worker: Tid(std.concurrency.MessageBox)
Tid myWorker = spawn(&worker);//线程返回工作者的线标.可供通信.
所有者的线标为ownerTid
.
send() 改善消息而 receiveOnly()接收消息
,还有prioritySend(), receive(), 和 receiveTimeout()
void main() {
Tid worker = spawn(&workerFunc);
foreach (value; 1 .. 5) {
worker.send(value);//给工作者发消息
double result = receiveOnly!double();
writefln("sent: %s, received: %s", value, result);
}
worker.send(-1);//工作者,可以休息了.
}
线程间通信
void workerFunc(){
int value = 0;
while(value> = 0){
value = receiveOnly!int();
double result = to!double(value)/ 5;
ownerTid.send(结果);//给主发
}
}
工作者,这很类似协程
啊,不过是有主从
关系的.
ownerTid.send(thisTid, 42, 1.5);//本标识
发多个.接收多个
auto message = receiveOnly!(Tid,int,double)();
//要加上类型.
auto sender = message[0];
auto integer = message[1];
auto floating = message[2];
如类型不一样,则报错.
import std.concurrency;
void workerFunc() {
ownerTid.send("hello");//发送串
}
void main() {
spawn(&workerFunc);
auto message = receiveOnly!double();//要双精
//消息不一样,抛异常,可以要求工作者处理异常
}
示例:机器人.
import std.stdio;
import std.random;
import std.string;
import std.concurrency;
import core.thread;
struct Position {
int line;
int column;
string toString() {
return format("%s,%s", line, column);
}
}
struct Movement {
Position from;
Position to;
string toString() {
return ((from == to)
? format("%s (idle)", from)
: format("%s -> %s", from, to));
}
}
class Robot {
string image;
Duration restDuration;
this(string image, Duration restDuration) {
this.image = image;
this.restDuration = restDuration;
}
override string toString() {
return format("%s(%s)", image, restDuration);
}
}
/*返回0,0附近随机值.*/
Position randomPosition() {
return Position(uniform!"[]"(-10, 10),
uniform!"[]"(-10, 10));
}
/* 从指定坐标至多返回一步. */
int randomStep(int current) {
return current + uniform!"[]"(-1, 1);
}
//从指定位置返回8个方向的随机邻居,或自身
Position randomNeighbor(Position position) {
return Position(randomStep(position.line),
randomStep(position.column));
}
struct Job {
size_t robotId;
Position origin;
Duration restDuration;
}
struct MovementMessage {
size_t robotId;
Movement movement;
}
void robotMover(Job job) {
Position from = job.origin;
while (true) {
Thread.sleep(job.restDuration);
Position to = randomNeighbor(from);
Movement movement = Movement(from, to);
from = to;
ownerTid.send(MovementMessage(job.robotId, movement));
}
}
void main() {
//不同方向的机器
Robot[] robots = [ new Robot("A", 600.msecs),
new Robot("B", 2000.msecs),
new Robot("C", 5000.msecs) ];
//为每个机器开启个移动线程
foreach (robotId, robot; robots) {
spawn(&robotMover, Job(robotId, randomPosition(), robot.restDuration));
}
//准备收集机器移动信息
while (true) {
auto message=receiveOnly!MovementMessage();
//打印机器移动
writefln("%s %s",robots[message.robotId], message.movement);
}
}
可见,工作者是独立的,所有者线程通过消息盒接收到的消息来序化消息
receiveOnly
仅可接收一种消息.而receive
可接收多种消息.它把消息分发给消息闭包,当消息来了,比较每个闭包的消息类型,匹配的闭包就处理它.
void workerFunc() {
bool isDone = false;
while (!isDone) {
void intHandler(int message) {
writeln("处理整: ", message);
if (message == -1) {
writeln("exiting");isDone = true;
}
}//这是嵌套函数.
void stringHandler(string message) {
writeln("处理串: ", message);
}
receive(&intHandler, &stringHandler);
}
}
测试:
import std.stdio;
import std.concurrency;
// ...
void main() {
auto worker = spawn(&workerFunc);
worker.send(10);worker.send(42);
worker.send("hello");worker.send(-1);//终止
}
还可使用λ和opCall
,receive
接收3个λ
.
import std.stdio;
import std.concurrency;
struct Exit {
}
void workerFunc() {
bool isDone = false;
while (!isDone) {
receive(
(int message) {
writeln("int message: ", message);
},
(string message) {
writeln("串 message: ", message);
},
(Exit message) {
writeln("exiting");isDone = true;
}
);//接收多个入.
}
}
void main() {
auto worker = spawn(&workerFunc);
worker.send(10);worker.send(42);
worker.send("hello");
worker.send(Exit());//改进了-1.
}
接收任意类型.
import std.stdio;
import std.concurrency;
void workerFunc() {
receive(
(int message) { /* ... */ },
(double message) { /* ... */ },
(Variant message) {//作为最后的解决方式
writeln("未期望消息: ", message);
});
}
struct SpecialMessage {
// ...
}
void main() {
auto worker = spawn(&workerFunc);
worker.send(SpecialMessage());
}
接收超时receiveTimeout
,防止一直等待.
import std.stdio;
import std.concurrency;
import core.thread;
void workerFunc() {
Thread.sleep(3.seconds);
ownerTid.send("你好");
}
void main() {
spawn(&workerFunc);writeln("等待消息");
bool received = false;
while (!received) {
received = receiveTimeout(600.msecs, (string message) { writeln("received: ", message); });
//第1个参数为超时时间.第2个
if (!received) {
writeln("... 没有消息");
/* ... 其他操作 ... */
}//不断循环等,没等到,执行
}//等到了执行入函数.
}
工作者执行时的异常.
try {
theTask.yieldForce();
} catch (Exception exc) {
writefln("检查到错误: '%s'",exc.msg);
}
任务抛了,主线程抓住,再抛.
在并发中.工作者可显式抓和抛异常
.
可以当作消息一样接收OwnerTerminated和LinkTerminated
.
void calculate() {
while (true) {
auto message = receiveOnly!string();
ownerTid.send(to!double(message) + 0.5);
}
}
import std.stdio;
import std.concurrency;
import std.conv;
// ...
void main() {
Tid calculator = spawn(&calculate);
calculator.send("1.2");
calculator.send("hello"); // 错误输入
calculator.send("3.4");
foreach (i; 0 .. 3) {
auto message = receiveOnly!double();
writefln("result %s: %s", i, message);
}
}
处理错误:
import std.stdio;
import std.concurrency;
import std.conv;
struct CalculationFailure {
string reason;
}
struct Exit {
}
void calculate() {
bool isDone = false;
while (!isDone) {
receive(
(string message) {
try {
ownerTid.send(to!double(message) + 0.5);
} catch (Exception exc) {
ownerTid.send(CalculationFailure(exc.msg));
}//处理两类,串和异常
},
(Exit message) {
isDone = true;
});
}
}
void main() {
Tid calculator = spawn(&calculate);
calculator.send("1.2");
calculator.send("hello"); // ← incorrect input
calculator.send("3.4");
calculator.send(Exit());
foreach (i; 0 .. 3) {
writef("结果%s: ", i);
receive(//处理两类
(double message) {
writeln(message);
},
(CalculationFailure message) {
writefln("错误!'%s'", message.reason);
});
}
}
或者再抛异常:
// ... at the worker ...
try {
// ...
} catch (shared(Exception) exc) {
ownerTid.send(exc);
}},
// ... at the owner ...
receive(
// ...
(shared(Exception) exc) {
throw exc;
});
检测线程终止
.
import std.stdio;
import std.concurrency;
void main() {
spawn(&intermediaryFunc);
}
void intermediaryFunc() {
auto worker = spawn(&workerFunc);
worker.send(1);
worker.send(2);
}//发送后终止
void workerFunc() {
while (true) {//主结束后,抛异常
auto m = receiveOnly!int();
writeln("Message: ", m);
}
}
工作者可抓
void workerFunc() {
bool isDone = false;
while (!isDone) {
try {
auto m = receiveOnly!int();
writeln("消息: ", m);
} catch (OwnerTerminated exc) {
writeln("所有者终止了.");
isDone = true;
}
}
}
链接终止
import std.stdio;
import std.concurrency;
void main() {
auto worker = spawnLinked(&workerFunc);//工作
while (true) {
auto m=receiveOnly!int();//终止后抛异常
writeln("消息:", m);
}
}
void workerFunc() {
ownerTid.send(10);
ownerTid.send(20);
}//结束后就断开了,这里是工作者断开
主,工断开时,工要抛个异常.主就可以抓住它.
bool isDone = false;
while (!isDone) {
try {
auto m = receiveOnly!int();
writeln("消息: ", m);
} catch (LinkTerminated exc) {
writeln("结束了");
isDone = true;
}
}
也可以将异常作为消息.
bool isDone = false;
while (!isDone) {
receive(
(int message) {
writeln("Message: ", message);
},
(OwnerTerminated exc) {
writeln("主完蛋了.");
isDone = true;
}
);
}
管理邮箱
线程都有私有邮箱,用于保存发送到该线程的邮件.
setMaxMailboxSize
设置最大大小.三个参数为邮箱,最大容量,和满时怎么处理
.
OnCrowding.block
,等待有空间.
OnCrowding.ignore
,丢弃.
OnCrowding.throwException
抛异常.
bool function(Tid)
,接收λ,调用指定函数.
//警告,系统可能会不稳定
import std.concurrency;
import core.thread;
void workerFunc() {
while (true) {
ownerTid.send(42); //不断发消息
}
}
void main() {
spawn(&workerFunc);
while (true) {//不断接收消息,又没处理完,
receive(
(int message) {//处理
Thread.sleep(1.seconds);
});
}//不断的消耗内存.
}
设置主线程的邮箱大小.
void main() {//主线程
setMaxMailboxSize(thisTid, 1000, OnCrowding.block);//多了,就等待
spawn(&workerFunc);
// ...
}
下面这个,满了抛异常.
import std.concurrency;
import core.thread;
void workerFunc() {
while (true) {
try {
ownerTid.send(42);
} catch (MailboxFull exc) {//失败,重来;
Thread.sleep(1.msecs);
}
}
}
void main() {
setMaxMailboxSize(thisTid, 1000, OnCrowding.throwException);
spawn(&workerFunc);
while (true) {
receive(
(int message) {
Thread.sleep(1.seconds);
});
}
}
消息优先级.
prioritySend(ownerTid, ImportantMessage(100));
重要消息.优先处理.接收者无处理器,则抛PriorityMessageException
异常.
线程名
作为线程的标记.可在任何线程内全局访问他们.
三个函数:
register
将名字与线程关联起来.
locate
,返回关联名的线程.如果没有,则为Tid.init
.
unregister
.取消关联.
import std.stdio;
import std.concurrency;
import core.thread;
struct Exit {
}
void main() {
// 命名为first,伙伴线程为"second"?
auto first = spawn(&player, "second");
register("first", first);
scope(exit) unregister("first");
// 命名为second,伙伴线程为"first"?
auto second = spawn(&player, "first");
register("second", second);
scope(exit) unregister("second");
Thread.sleep(2.seconds);
prioritySend(first, Exit());
prioritySend(second, Exit());
//为了注销成功,主等待工作者结束
thread_joinAll();
}
void player(string nameOfPartner) {
Tid partner;
while (partner == Tid.init) {
Thread.sleep(1.msecs);
partner = locate(nameOfPartner);
}
bool isDone = false;
while (!isDone) {
partner.send("hello " ~ nameOfPartner);
receive(
(string message) {
writeln("Message: ", message);
Thread.sleep(500.msecs);
},
(Exit message) {
writefln("%s,我退出了.", nameOfPartner);
isDone = true;
});
}
}
启动两个线程,按名字找对方,不断发消息,直到退出
.
线程独立时,考虑并行;线程需要通信时,考虑并发
共享数据很难正确实现,所以更喜欢通过消息来并发
spawn() 和 spawnLinked()
开启线程
thisTid
,当前线程线标,
ownerTid
,拥有者线标,
send() 和 prioritySend()
发送消息.
receiveOnly(), receive(), 和 receiveTimeout()
接收消息.
Variant
匹配任何消息.
setMaxMailboxSize
限制邮箱大小
register(), unregister(), 和 locate()
允许按名引用线程
传递消息时,可抛以下MessageMismatch, OwnerTerminated, LinkTerminated, MailboxFull, 和 PriorityMessageException
异常
拥有者线程不能自动抓工作者抛出的异常.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现