2

不久前,我整理了一个名为的简单类Actor,它是我对Actor Model的实现。从那以后,我使用它取得了巨大的成功(减去一些因缺乏可区分的联合类型而导致的烦人的解决方法。)。我有一个问题,我不确定如何在不让课堂变得笨拙和缓慢的情况下解决。

当某人定义消息时,他们当然有权包含对调用者自己可以操作的对象的引用。即使知道我几乎是唯一使用这个类的人,这仍然困扰着我。

解决这个问题的一个很好的例子是在 Firefox 中实现的Web Workers 。将对象传递给工作人员时,它会被序列化为 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);
        }
    }
}
4

2 回答 2

1

并不是说这是一个很好的解决方案,但是您可以在获取对象时限制T为并克隆该对象。ICloneable这或多或少相当于您描述的“序列化为 JSON”方法的意识形态。不用说,这将是笨重和缓慢的。

真的,你应该记住保持消息不可变。C# 不是一种可以很好地为您执行此操作的语言。

于 2010-06-20T02:29:34.663 回答
0

我认为您在这里没有问题-如果您的身上发生了任何类型的序列化,_messageChannel那么您的被调用者将不会获得对原始对象的引用,他们将获得原始对象的副本。

如果在调用完成后将“引用”返回给您很重要,那么您可能需要考虑使用标识符而不是仅仅依赖类引用,然后使用该标识符来定位原始对象。

于 2010-06-20T02:33:02.380 回答