Какой-то Async Socket Code - Помощь с сборкой мусора? - PullRequest
3 голосов
/ 02 ноября 2009

Я думаю, что этот вопрос действительно касается моего понимания Сборка мусора и ссылок на переменные . Но я пойду вперед и выкину какой-нибудь код для вас.

// Обратите внимание, не используйте этот код для асинхронных сокетов, просто чтобы выделить мой вопрос

// 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. Будет ли socketTransport_One собираться сборщиком мусора при выходе из метода OnReadOne? Даже при том, что он все еще содержит сильную ссылку на ~ SocketA ~

Спасибо всем!

Ответы [ 3 ]

5 голосов
/ 16 апреля 2010

В вашем примере socketTransport_One должен быть собранным мусором, потому что никакие другие объекты не имеют строгой ссылки на it . Тот факт, что он имеет сильную ссылку на другой объект, не означает, что он не подходит для сборки мусора.

3 голосов
/ 21 апреля 2010

Для получения дополнительной информации о том, как работает сборщик мусора, см. http://msdn.microsoft.com/en-us/library/ee787088.aspx#what_happens_during_a_garbage_collection

2 голосов
/ 22 апреля 2010

Адам прав, socketTransport_One имеет право на сборку мусора, как только OnReadOne() выйдет. Тем не менее, соответствие не означает, что сборка мусора действительно произойдет именно тогда.

Брайан тоже прав, в том, что вы всегда должны вызывать EndReceive (пара EndX для любого метода BeginX в целом). Это в соответствии с MSDN. Однако в текущей реализации вы не потеряете ресурсы, даже если вам не удастся вызвать EndReceive. AsyncState освобождается, как только ваш обратный вызов завершается. Но опять же, вы не должны полагаться на это.

@ Брайан: О сокете, оставленном без ссылок, когда у него еще есть работа: он тоже будет собирать мусор. Его 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);
            }
        }
    }
}
...