一些异步套接字代码 - 帮助垃圾收集?

时间:2009-11-02 11:40:20

标签: c# variables asynchronous garbage-collection sockets

我认为这个问题实际上是关于我对垃圾收集变量引用的理解。但是我会继续推出一些代码供你查看。

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

// 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~

  • 的强引用

全部谢谢!

3 个答案:

答案 0 :(得分:5)

在您的示例中,socketTransport_One 进行垃圾收集,因为没有其他对象具有对 it 的强引用。仅仅因为它对另一个对象的强引用并不意味着它没有资格进行垃圾收集。

答案 1 :(得分:3)

有关垃圾收集器如何工作的更多信息,请参阅 http://msdn.microsoft.com/en-us/library/ee787088.aspx#what_happens_during_a_garbage_collection

答案 2 :(得分:2)

Adam是对的,只要socketTransport_One退出,OnReadOne()就有资格进行垃圾回收。但是,资格并不意味着垃圾收集实际上就会发生。

Brian也是对的,因为您应该始终致电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);
            }
        }
    }
}