C # Асинхронный TCP и сборка мусора своего экземпляра, на который ссылается закрытие? - PullRequest
3 голосов
/ 07 марта 2011

Просто интересно в случае асинхронного TCP или другого шаблона EAP, если обработчик успеха имеет ссылку this, например, this.state, теоретически, есть ссылка на текущий экземпляр, так как this сохраняется в некоторой сгенерированной области объекта по природе замыкания. Таким образом, сам экземпляр не должен подвергаться сборке мусора, даже если область, в которой он создан, завершена?

Мой код похож на следующий:

 public class ATcpClient
    {
        private ATcpState state = null;

        private void Receive()
        {
            // create the callback here, in order to use in dynamic
            AsyncCallback ReceiveCallback = delegate(IAsyncResult ar)
                {
                    try
                    {
                        // Read data from the remote device.
                        this.state.BytesReceived = this.state.Socket.EndReceive(ar);

                    }
                    catch (Exception e)
                    {
                        // ...

                    }
                };

            try
            {
                    this.state.Socket.BeginReceive(this.state.Buffer, 0, this.state.BufferSize, 0,
                        ReceiveCallback, null);
            }
            catch (Exception e)
            {
                // ...
                // ...
            }
        }
}

код, который его выполняет, может выглядеть так:

public void DoExecuteCode()
{
    new ATcpClient().Receive();
}

Будет ли экземпляр GC, что приведет к сбою Receive () в целом?

1 Ответ

2 голосов
/ 07 марта 2011

Это зависит от того, насколько умный компилятор.

В вашем случае, да, this определенно будет поддерживаться делегатом в течение всего срока его жизни.

Давайте рассмотрим случай, который определенно НЕ сохранит this в живых:

    private void Receive()
    {
        ATcpState state = this.state;
        // create the callback here, in order to use in dynamic
        AsyncCallback ReceiveCallback = delegate(IAsyncResult ar)
            {
                try
                {
                    // Read data from the remote device.
                    state.BytesReceived = state.Socket.EndReceive(ar);

                }
                catch (Exception e)
                {
                    // ...

                }
            };

        try
        {
                state.Socket.BeginReceive(state.Buffer, 0, state.BufferSize, 0,
                    ReceiveCallback, null);
        }
        catch (Exception e)
        {
            // ...
            // ...
        }
    }

А затем случай, когда оптимизация компилятора может повлиять на поведение коллекции:

    private readonly ATcpState state = new ATcpState();
    private void Receive()
    {
        // create the callback here, in order to use in dynamic
        AsyncCallback ReceiveCallback = delegate(IAsyncResult ar)
            {
                try
                {
                    // Read data from the remote device.
                    state.BytesReceived = state.Socket.EndReceive(ar);

                }
                catch (Exception e)
                {
                    // ...

                }
            };

        try
        {
                state.Socket.BeginReceive(state.Buffer, 0, state.BufferSize, 0,
                    ReceiveCallback, null);
        }
        catch (Exception e)
        {
            // ...
            // ...
        }
    }

Единственный оставшийся вопрос: каков срок службы этого делегата? Является ли ожидающая операция корнем, или мы просто имеем циклическую ссылку между делегатом, возможно this, state, state.Socket, и операцией? Если ни один из них не доступен из корня, тогда весь пакет может быть завершен (что закроет сокет, отменяя операцию).

По крайней мере для некоторых объектов, использующих шаблон BeginReceive / EndReceive, операция НЕ является корневой

Похоже, что для вашего случая (Socket.BeginReceive), внутри System.Net.Sockets.BaseOverlappedAsyncResult.PinUnmanagedObjects.

создается корень.
...