2009-11-02 6 views
3

Je pense que cette question est vraiment sur ma compréhension de Garbage collection et variables références. Mais je vais aller de l'avant et jeter un code pour vous de regarder.Code de socket asynchrone - Aide pour la récupération de mémoire

// S'il vous plaît noter ne pas utiliser ce code pour les sockets async, pour mettre en évidence ma question

// SocketTransport 
// This is a simple wrapper class that is used as the 'state' object 
// when performing Async Socket Reads/Writes 
public class SocketTransport 
{ 
    public Socket Socket; 
    public byte[] Buffer; 
    public SocketTransport(Socket socket, byte[] buffer) 
    { 
     this.Socket = socket; 
     this.Buffer = buffer; 
    } 
} 

// Entry point - creates a SocketTransport, then passes it as the state 
// object when Asyncly reading from the socket. 
public void ReadOne(Socket socket) 
{ 
    SocketTransport socketTransport_One = 
     new SocketTransport(socket, new byte[10]); 

    socketTransport_One.Socket.BeginRecieve 
     (
     socketTransport_One.Buffer, // Buffer to store data 
     0,        // Buffer offset 
     10,       // Read Length 
     SocketFlags.None    // SocketFlags 
     new AsyncCallback(OnReadOne), // Callback when BeginRead completes 
     socketTransport_One   // 'state' object to pass to Callback. 
     ); 
} 

public void OnReadOne(IAsyncResult ar) 
{ 
    SocketTransport socketTransport_One = ar.asyncState as SocketTransport; 
    ProcessReadOneBuffer(socketTransport_One.Buffer); // Do processing 

    // New Read 
    // Create another! SocketTransport (what happens to first one?) 
    SocketTransport socketTransport_Two = 
     new SocketTransport(socket, new byte[10]); 

    socketTransport_Two.Socket.BeginRecieve 
     (
     socketTransport_One.Buffer, 
     0, 
     10, 
     SocketFlags.None 
     new AsyncCallback(OnReadTwo), 
     socketTransport_Two 
     ); 
} 

public void OnReadTwo(IAsyncResult ar) 
{ 
    SocketTransport socketTransport_Two = ar.asyncState as SocketTransport; 
    .............. 

Ma question est la suivante:

  • La première SocketTransport à créer (socketTransport_One) a une forte référence à un objet Socket (appel est ~ SocketA ~). Une fois la lecture asynchrone terminée, un nouvel objet SocketTransport est créé (socketTransport_Two) avec une référence forte à ~ SocketA ~.

    Q1. SocketTransport_One sera-t-il collecté par le garbage collector lorsque la méthode OnReadOne sera fermée? Même s'il contient encore une forte référence à ~ SocketA ~

Merci à tous!

Répondre

2

Adam a raison, socketTransport_One est admissible à la collecte des ordures dès OnReadOne() sorties. Cependant, l'éligibilité ne signifie pas que la collecte des ordures aura lieu à ce moment-là. Brian a raison également, en ce que vous devez toujours appeler EndReceive (la paire EndX à toute méthode BeginX en général). C'est en fonction de MSDN. Cependant, dans la mise en œuvre actuelle, vous ne fuirez aucune ressource même si vous ne parvenez pas à appeler EndReceive. L'AsyncState est publié dès que votre rappel se termine. Mais encore une fois, vous ne devriez pas compter sur cela.

@Brian: A propos d'un Socket laissé sans références quand il a encore du travail à faire: il va aussi être ramassé. Sa méthode Dispose() pouvait attendre que les opérations en attente se terminent, mais je pense que cette fonctionnalité est désactivée pour le moment. Donc, vous ne fuirez rien ici non plus.

Je l'ai mis en place un petit jouet à jouer avec, je l'espère, il aide à éclaircir les choses encore plus:

using System; 
using System.Net.Sockets; 
using System.Net; 
using System.Text; 
using System.Threading; 

namespace ConsoleApplication95 
{ 
    class MySocket : Socket 
    { 
     readonly string name; 
     public string Name 
     { 
      get { return name; } 
     } 

     public MySocket(string newName, AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) 
      : base(addressFamily, socketType, protocolType) 
     { 
      name = newName; 
     } 

     protected override void Dispose(bool disposing) 
     { 
      Console.WriteLine("Socket " + Name + " disposing"); 
      base.Dispose(disposing); 
     } 
    } 

    class Program 
    { 
     static TcpListener listener; 

     static void Main(string[] args) 
     { 
      listener = new TcpListener(IPAddress.Any, 2055); 
      listener.Start(); 

      Thread t = new Thread(TcpService); 
      t.Start(); 

      Console.WriteLine("TCP server started, listening to port 2055"); 

      SocketTransport tr = new SocketTransport("1", new MySocket("1", AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp), new byte[64]); 
      tr.Socket.Connect(IPAddress.Loopback, 2055); 
      tr.Socket.BeginReceive(tr.Buffer, 0, tr.Buffer.Length, SocketFlags.None, OnReadOne, tr); 
      tr = null; 

      Console.WriteLine("Press enter to trigger GC"); 
      Console.ReadLine(); 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      Console.WriteLine("Press enter to exit"); 
      Console.ReadLine(); 
     } 

     public class SocketTransport : IDisposable 
     { 
      public Socket Socket; 
      public byte[] Buffer; 
      public string Name; 
      public SocketTransport(string name, Socket socket, byte[] buffer) 
      { 
       Name = name; 
       Socket = socket; 
       Buffer = buffer; 
      } 

      public void Dispose() 
      { 
       Console.WriteLine("SocketTransport " + Name + " disposing"); 
      } 

      ~SocketTransport() 
      { 
       Dispose(); 
      } 
     } 

     public static void OnReadOne(IAsyncResult ar) 
     { 
      SocketTransport tr = ar.AsyncState as SocketTransport; 
      string message = Encoding.ASCII.GetString(tr.Buffer); 
      Console.WriteLine("OnReadOne: " + message); 
      Socket socket = tr.Socket; 

      ar = null; 
      tr = null; 
      // SocketTransport 1 would become eligible for garbage collection here 
      // if the caller of OnReadOne didn't hold a reference as a local variable. 
      // 
      // As soon as we exit from this method, our caller exits too 
      // and the local reference will be no more and SocketTransport 1 
      // can be garbage collected. It doesn't matter whether we 
      // call EndReceive or not, as illustrated with the triggered GC 
      // in OnReadTwo() and the one after pressing enter in Main. 

      SocketTransport tr2 = new SocketTransport("2", socket, new byte[64]); 
      tr2.Socket.BeginReceive(tr2.Buffer, 0, tr2.Buffer.Length, SocketFlags.None, OnReadTwo, tr2); 
     } 

     public static void OnReadTwo(IAsyncResult ar) 
     { 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 

      SocketTransport tr = ar.AsyncState as SocketTransport; 
      tr.Socket.EndReceive(ar); 
      string message = Encoding.ASCII.GetString(tr.Buffer); 
      Console.WriteLine("OnReadTwo: " + message); 
     } 

     public static void TcpService() 
     { 
      using (Socket socket = listener.AcceptSocket()) 
      { 
       socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 10000); 

       Console.WriteLine("Connected: {0}", socket.RemoteEndPoint); 
       try 
       { 
        socket.NoDelay = true; 
        socket.Send(Encoding.ASCII.GetBytes("message 1")); 
        Thread.Sleep(100); 
        socket.Send(Encoding.ASCII.GetBytes("message 2")); 
       } 
       catch (Exception e) 
       { 
        Console.WriteLine(e.Message); 
       } 

       Console.WriteLine("Disconnecting: {0}", socket.RemoteEndPoint); 
      } 
     } 
    } 
} 
5

Dans votre exemple, socketTransport_Onedevrait être ramasse-miettes, parce qu'aucun autre objet ont une forte référence à il. Ce n'est pas parce qu'il a une forte référence à un autre objet qu'il n'est pas éligible pour le ramassage des ordures.

+0

Salut Adam, merci pour l'aide. Si vous avez raison et que socketTransport_One est récupéré, est-ce que Socket sera Dispose() d? Même si elle est utilisée dans socketTransport_Two? – divinci

+0

Non, car 'socketTransport_Two' aura maintenant une forte référence à la socket. Il ne sera pas collecté parce qu'il est encore référencé. –

+0

@divinci La seule façon dont le Socket sera éliminé lorsque le SocketTransport est garbage collect (ce que Brian a mentionné dans une autre réponse, peut ne pas se produire sans appel à EndInvoke) est si la classe SocketTransport a un finalizer qui appelle explicitement Dispose sur le Socket qu'elle possède. –

Questions connexes