Как я могу обойти мою проблему "ссылки на объект" при передаче сообщений актеру? - PullRequest
2 голосов
/ 20 июня 2010

Некоторое время назад я собрал простой класс с именем Actor, который был моей реализацией Actor Model . С тех пор я использовал его с большим успехом ( Минус некоторые раздражающие обходные пути из-за отсутствия распознаваемого типа объединения. ). У меня осталась проблема, которую я не знаю, как решить, не делая класс неуклюжим и медленным.

Когда кто-то определяет сообщение, он, конечно, имеет право включать ссылку на объект, которым может манипулировать сам вызывающий объект. Даже со знанием того, что я буду единственным человеком, использующим этот класс, это все равно беспокоит меня.

Хороший пример того, как это сделать, - Web Workers , реализованный в Firefox. При передаче объекта работнику он сериализуется в JSON.

Есть идеи?

public abstract class Actor<T, U> : IDisposable
{
    private const int AsyncChannelPoolSize = 20;

    private volatile bool _disposed;
    private readonly Thread _actorThread;
    private readonly AsyncReplyChannel<T, U> _messageChannel;
    private readonly Lazy<ObjectPool<AsyncChannel<U>>> _asyncChannelPool;


    public event EventHandler<ExceptionEventArgs> Exception;


    protected Actor()
    {
        _messageChannel = new AsyncReplyChannel<T, U>();
        _asyncChannelPool = new Lazy<ObjectPool<AsyncChannel<U>>>(() => new ObjectPool<AsyncChannel<U>>(AsyncChannelPoolSize));
        _actorThread = new Thread(ProcessMessages);
        _actorThread.IsBackground = true;
        _actorThread.Start();
    }


    public U PostWithReply(T value)
    {
        ThrowIfDisposed();

        var replyChannel = default(AsyncChannel<U>);
        var replyPackage = default(AsyncReplyPackage<T, U>);
        var replyMessage = default(U);

        try
        {
            replyChannel = _asyncChannelPool.Value.Get();
            replyPackage = new AsyncReplyPackage<T, U>(value, replyChannel);
            _messageChannel.Send(replyPackage);
            replyMessage = replyChannel.Receive();
        }
        finally
        {
            _asyncChannelPool.Value.Put(replyChannel);
        }

        return replyMessage;
    }

    public void PostWithAsyncReply(T value, IAsyncChannel<U> replyChannel)
    {
        ThrowIfDisposed();
        _messageChannel.Send(new AsyncReplyPackage<T, U>(value, replyChannel));
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected abstract void ProcessMessage(AsyncReplyPackage<T, U> package);

    protected virtual void OnException(Exception ex)
    {
        var exceptionEvent = Exception;
        if (exceptionEvent != null)
        {
            exceptionEvent(this, new ExceptionEventArgs(ex));
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        _disposed = true;
        _messageChannel.Dispose();
        if (_asyncChannelPool.IsValueCreated)
        {
            _asyncChannelPool.Value.Dispose();
        }
    }

    private void ProcessMessages()
    {
        var package = default(AsyncReplyPackage<T, U>);
        while (_messageChannel.TryReceive(out package) && !_disposed)
        {
            try
            {
                ProcessMessage(package);
            }
            catch (Exception ex)
            {
                OnException(ex);
            }
        }
    }

    private void ThrowIfDisposed()
    {
        if (_disposed)
        {
            throw new ObjectDisposedException(GetType().FullName);
        }
    }
}

Ответы [ 2 ]

1 голос
/ 20 июня 2010

Не то чтобы это хорошее решение, но вы можете ограничить T значением ICloneable и клонировать объект, когда получите его.Это более или менее идеологический эквивалент подхода «сериализации в JSON», который вы описали.Само собой разумеется, это было бы неуклюже и медленно.

Действительно, вы должны просто помнить, чтобы сообщения оставались неизменными.C # не является языком, который может обеспечить это очень хорошо для вас.

0 голосов
/ 20 июня 2010

Я думаю, у вас здесь нет проблем - если на вашем _messageChannel происходит какая-либо сериализация, то ваш собеседник не будет получать ссылку на исходный объект, он будет получать копию исходного объекта.

Если важно, чтобы вы получили эту «ссылку», возвращенную вам после завершения вызова, тогда вы можете подумать об использовании идентификатора, а не просто полагаться на ссылку на класс, а затем использовать этот идентификатор для определения местоположения исходного объекта. .

...