2010-06-20 27 views
2

Il ya quelque temps, j'ai mis en place une classe simple nommée Actor qui était ma mise en œuvre du Actor Model. Depuis lors, je l'ai utilisé avec beaucoup de succès (Moins certaines solutions de contournement ennuyeuses pour l'absence d'un type d'union discriminé.)). Je suis à gauche avec un problème que je ne sais pas comment résoudre sans rendre la classe maladroite et lente.Comment puis-je contourner mon problème de "référence d'objet" lors de la transmission de messages à un acteur?

Lorsqu'une personne définit un message, elle a bien entendu le droit d'inclure une référence à un objet que l'appelant peut lui-même manipuler. Même avec la certitude que je serai la seule personne à utiliser cette classe, cela me dérange toujours.

Un bon exemple d'un moyen de contourner cela est avec Web Workers tel que mis en œuvre dans Firefox. Lors du passage d'un objet à un worker, il est sérialisé en JSON.

Des idées?

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); 
     } 
    } 
} 

Répondre

1

Non que ce soit une bonne solution, mais vous pouvez restreindre T être ICloneable et cloner l'objet lorsque vous l'obtenez. C'est plus ou moins l'équivalent idéologique de l'approche «serialize to JSON» que vous avez décrite. Inutile de dire que ce serait maladroit et lent. Vraiment, vous devriez juste vous rappeler de garder les messages immuables. C# n'est pas un langage qui peut très bien l'appliquer pour vous.

+0

J'ai tendance à faire tout ce que je peux immuable. Je pense que c'est plus une question de type de pureté théorique si cela a du sens. J'ai l'impression que vous avez dit que C# n'a vraiment aucune solution à ce problème qui fonctionnerait très bien. – ChaosPandion

+0

Je vais accepter cette réponse pour être le vérificateur des mauvaises nouvelles. – ChaosPandion

0

Je pense que vous n'avez pas de problème ici - si une sorte de sérialisation se produit sur votre _messageChannel alors votre destinataire ne recevra pas de référence à l'objet original, il recevra une copie de l'objet original. S'il est important que vous obteniez cette "référence" à la fin de l'appel, vous voudrez peut-être penser à utiliser un identifiant au lieu de simplement vous fier à la référence de la classe, puis utilisez cet identifiant pour localiser l'original. objet.

+0

Pour des raisons de performances, je n'effectue aucune sérialisation et je me base sur ma propre discipline pour définir uniquement les messages immuables. – ChaosPandion