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异常
拥有者线程不能自动抓工作者抛出的异常.

发布了440 篇原创文章 · 获赞 29 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/fqbqrr/article/details/104590177