CCRでサンタクロース問題

以前からMicrosoft Robotics Developer Studioに含まれるCCR(Concurrency and Coordination Runtime)という技術に興味があったんですが、最近機会があったので少し調べてみました。

こちらの記事によると、CCRは、Erlangというプログラミング言語アクターモデルというものを参考にしているようです。

アクターモデルとは、スレッドよりも軽量で、高速に生成・破棄・メッセージ送信ができるアクターという概念を利用して、並行処理を実現するモデルのようです。

CCRでは、これを実現するために、PortとPortSetというメッセージキューの仕組み、DispatcherとDispatcherQueueというタスクを並行に実行する仕組み、ユーザコードとタスク実行の仲介役であるArbiterなどを用意しています。

さて、このCCRを利用するといったいどういうことができるのでしょうか?


ちょっと検索してみると、1年半くらい前にErlangScalaを利用して、サンタクロース問題を解くというのが流行っていたようです。

というわけで、CCRを利用して解いてみました。ビルド・実行するためには、Robotics Developer Studioに含まれるMicrosoft.Ccr.Core.dllを参照する必要があります。

using System;
using System.Linq;
using System.Threading;
using Microsoft.Ccr.Core;

public class CcrSanta
{
    static void Main()
    {
        var dispatcher = new Dispatcher(20, "dispatcher");
        var taskQueue = new DispatcherQueue("taskQueue", dispatcher);
        var fromReindeer = new Port<int>();
        var fromElf = new Port<int>();
        var toReindeer = new Port<int>[9];
        var toElf = new Port<int>[10];
        var rand = new Random();

        // となかい9頭のお仕事
        for (int i = 0; i < 9; i++)
        {
            toReindeer[i] = new Port<int>();
            Arbiter.Activate(
                taskQueue,
                Arbiter.Receive(
                    true,
                    toReindeer[i],
                    x =>
                    {
                        Console.WriteLine("となかい" + x + "は遊んでいます");
                        Thread.Sleep(rand.Next(10) * 1000);
                        Console.WriteLine("となかい" + x + "が戻ってきました");
                        fromReindeer.Post(x);
                    }));
        }

        // こびと10人のお仕事
        for (int i = 0; i < 10; i++)
        {
            toElf[i] = new Port<int>();
            Arbiter.Activate(
                taskQueue,
                Arbiter.Receive(
                    true,
                    toElf[i],
                    x =>
                    {
                        Console.WriteLine("こびと" + x + "は働いています");
                        Thread.Sleep(rand.Next(10) * 1000);
                        Console.WriteLine("こびと" + x + "がやってきました");
                        fromElf.Post(x);
                    }));
        }

        // サンタさんのお仕事
        Arbiter.Activate(
            taskQueue,
            Arbiter.MultipleItemReceive(true, fromReindeer, 9, x =>
            {
                Console.WriteLine("おもちゃを配りましょう", x.Length);
                x.ToList().ForEach(y => toReindeer[y].Post(y));
            }),
            Arbiter.MultipleItemReceive(true, fromElf, 3, x =>
            {
                Console.WriteLine("会議をしましょう", x.Length);
                x.ToList().ForEach(y => toElf[y].Post(y));
            })
        );

        for (int i = 0; i < 10; i++) toElf[i].Post(i);
        for (int i = 0; i < 9; i++) toReindeer[i].Post(i);
    }
}

スレッドやロックを用意せずに並行処理が書けるというのは不思議な感じですね。