我的Akka.Net演示速度非常慢

use*_*896 2 akka.net

我正在尝试使用akka.net运行概念证明.我确信我做的事情非常糟糕,但我无法弄清楚它是什么.

我希望我的演员形成一个节点图.稍后,这将是一个复杂的业务对象图,但是现在我想尝试一个简单的线性结构,如下所示:

在此输入图像描述

我想问一个节点是否有9步之遥的邻居.我试图以递归方式实现它.我向节点#9询问距离为9步的邻居,然后我向节点#8询问距离为8步的邻居,依此类推.最后,这应该返回节点#0作为答案.

好吧,我的代码有效,但执行时间超过4秒.这是为什么?

这是我的完整代码清单:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Akka;
using Akka.Actor;

namespace AkkaTest
{
    class Program
    {
        public static Stopwatch stopwatch = new Stopwatch();
        static void Main(string[] args)
        {
            var system = ActorSystem.Create("MySystem");

            IActorRef[] current = new IActorRef[0];

            Console.WriteLine("Initializing actors...");

            for (int i = 0; i < 10; i++)
            {
                var current1 = current;
                var props = Props.Create<Obj>(() => new Obj(current1, Guid.NewGuid()));
                var actorRef = system.ActorOf(props, i.ToString());
                current = new[] { actorRef };
            }
            Console.WriteLine("actors initialized.");

            FindNeighboursRequest r = new FindNeighboursRequest(9);

            stopwatch.Start();

            var response = current[0].Ask(r);
            FindNeighboursResponse result = (FindNeighboursResponse)response.Result;
            stopwatch.Stop();
            foreach (var d in result.FoundNeighbours)
            {
                Console.WriteLine(d);
            }

            Console.WriteLine("Search took " + stopwatch.ElapsedMilliseconds + "ms.");
            Console.ReadLine();
        }
    }
    public class FindNeighboursRequest
    {
        public FindNeighboursRequest(int distance)
        {
            this.Distance = distance;
        }
        public int Distance { get; private set; }
    }

    public class FindNeighboursResponse
    {
        private IActorRef[] foundNeighbours;

        public FindNeighboursResponse(IEnumerable<IActorRef> descendants)
        {
            this.foundNeighbours = descendants.ToArray();
        }

        public IActorRef[] FoundNeighbours
        {
            get { return this.foundNeighbours; }
        }
    }


    public class Obj : ReceiveActor
    {
        private Guid objGuid;
        readonly List<IActorRef> neighbours = new List<IActorRef>();
        public Obj(IEnumerable<IActorRef> otherObjs, Guid objGuid)
        {
            this.neighbours.AddRange(otherObjs);
            this.objGuid = objGuid;
            Receive<FindNeighboursRequest>(r => handleFindNeighbourRequest(r));
        }

        public Obj()
        {
        }

        private async void handleFindNeighbourRequest (FindNeighboursRequest r)
        {
            if (r.Distance == 0)
            {
                FindNeighboursResponse response = new FindNeighboursResponse(new IActorRef[] { Self });
                Sender.Tell(response, Self);
                return;
            }

            List<FindNeighboursResponse> responses = new List<FindNeighboursResponse>();

            foreach (var actorRef in neighbours)
            {
                FindNeighboursRequest req = new FindNeighboursRequest(r.Distance - 1);
                var response2 = actorRef.Ask(req);
                responses.Add((FindNeighboursResponse)response2.Result);
            }

            FindNeighboursResponse response3 = new FindNeighboursResponse(responses.SelectMany(rx => rx.FoundNeighbours));
            Sender.Tell(response3, Self);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Bar*_*ski 5

这种行为缓慢的原因是你使用Ask(你使用它的方式,但我将在稍后介绍).在您的示例中,您在循环中询问每个邻居,然后立即执行response2.Result哪个主动阻止当前actor(以及它所驻留的线程).所以你实际上是通过阻塞来实现同步流.

解决这个问题最简单的方法是收集所有返回的任务,Ask然后用Task.WhenAll它们全部收集它们,而不必等待循环中的每个任务.以此为例:

public class Obj : ReceiveActor
{
    private readonly IActorRef[] _neighbours;
    private readonly Guid _id;

    public Obj(IActorRef[] neighbours, Guid id)
    {
        _neighbours = neighbours;
        _id = id;
        Receive<FindNeighboursRequest>(async r =>
        {
            if (r.Distance == 0) Sender.Tell(new FindNeighboursResponse(new[] {Self}));
            else
            {
                var request = new FindNeighboursRequest(r.Distance - 1);
                var replies = _neighbours.Select(neighbour => neighbour.Ask<FindNeighboursResponse>(request));
                var ready = await Task.WhenAll(replies);
                var responses = ready.SelectMany(x => x.FoundNeighbours);
                Sender.Tell(new FindNeighboursResponse(responses.ToArray()));
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

这个更快.

注意:一般情况下,你不应该在演员里面使用Ask:

  1. 每个ask都是在当前actor中分配一个监听器,因此通常使用Ask比传递消息更重要Tell.
  2. 当通过演员链发送消息时,询问成本还通过每个演员传送两次消息(一个用于请求,一个用于回复).其中一种流行的模式是,当您从A⇒B⇒C⇒D发送请求并从D回复到A时,您可以直接回复D⇒A,而无需通过整个链回传递消息.通常组合Forward/ Tell效果更好.
  3. 一般情况下,如果没有必要,请不要使用Receive的异步版本 - 目前,与同步版本相比,演员的速度要慢一些.