2009-11-02 30 views
3

我觉得这个问题真的是我对的理解垃圾回收变量引用。但我会继续,并抛出一些代码供您查看。一些异步套接字代码 - 帮助垃圾收集?

//请注意不要使用异步套接字这段代码,只是为了突出我的问题

// 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; 
    .............. 

所以我的问题是:

  • 第一SocketTransport要创建(socketTransport_One)对Socket对象有强烈的引用(让我们调用〜SocketA〜)。

  • 一旦完成异步读取,就会创建一个新的SocketTransport对象(socketTransport_Two),并且对〜SocketA〜也有很强的参考。

    Q1。当方法OnReadOne退出时,socketTransport_One会被垃圾收集器收集吗?即使它仍然包含〜SocketA〜

谢谢大家!

回答

2

亚当是正确的更多信息,socketTransport_One只要OnReadOne()出口是符合垃圾回收。但是,资格并不意味着垃圾回收实际上会在那时发生。

布赖恩是对的,因为你应该总是打电话EndReceiveEndX对一般BeginX方法)。这是根据MSDN。但是,在目前的实施中,即使您未能拨打EndReceive,也不会泄漏任何资源。一旦你的回调完成,AsyncState就会被释放。但是再一次,你不应该依赖这个。

@Brian:关于一个Socket在没有引用时仍然有工作要做:它也将被垃圾收集。它的Dispose()方法可能会等待挂起的操作完成,但我认为此时此功能已被禁用。所以你也不会在这里泄漏任何东西。

我已经把一个小玩具玩,我希望它有助于澄清一些事情更:

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

在你的榜样,socketTransport_One应该是垃圾回收,因为没有其他物体具有很强的参考。仅仅因为它对另一个对象的强引用并不意味着它不适合垃圾收集。

+0

嗨亚当的帮助表示感谢。如果你是正确的,并且socketTransport_One被垃圾收集 - 它的Socket是Dispose()d吗?即使它在socketTransport_Two中使用? – divinci 2010-04-19 16:41:35

+0

不,因为'socketTransport_Two'现在将对套接字有强烈的引用。它不会被垃圾收集,因为它仍然被引用。 – 2010-04-19 19:44:15

+0

@divinci当SocketTransport被垃圾回收时(如Brian在另一个回答中提到的,没有对EndInvoke的调用可能不会发生),Socket将被丢弃的唯一方式是如果SocketTransport类具有明确调用Dispose的终结器它拥有的插座。 – 2010-04-21 16:05:52