2

しばらく前に、アクターモデルActorの実装であるという名前の単純なクラスをまとめました。それ以来、私はそれを大成功で使用してきました(識別された共用体タイプがないためのいくつかの厄介な回避策を除いて)。クラスを不格好で遅くせずに解決する方法がわからないという問題が残っています。

誰かがメッセージを定義するとき、彼らはもちろん、呼び出し元自身が操作できるオブジェクトへの参照を含める権利の範囲内にあります。このクラスを使用するのは私だけだという知識があっても、これはまだ私を悩ませます。

これを回避する良い例は、Firefoxに実装されているWebワーカーを使用することです。オブジェクトをワーカーに渡すと、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 に答える