2011-03-07 32 views
3

想知道在异步TCP或其他EAP模式的情况下,如果成功处理程序具有参考this,例如, this.state,理论上有一个对当前实例的引用,因为this被闭包性质保存在某个生成的对象作用域中。因此,即使创建实例的范围完成执行,实例本身也不应被垃圾收集?C#异步TCP和它的闭包引用实例的垃圾回收?

我的代码是类似以下内容:该执行它可以像

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的实例,它会导致接收()失败作为一个整体?

回答

2

这取决于编译器的聪明程度。

就你而言,是的,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) 
     { 
      // ... 
      // ... 
     } 
    } 

唯一剩下的问题是,该代表的一生是什么?挂起的操作是否为根,或者我们是否在委托人(可能为thisstatestate.Socket)和操作之间有循环引用?如果这些都不能从根到达,那么可以完成整个堆栈(这将关闭套接字,取消操作)。

At least for some objects using the BeginReceive/EndReceive pattern, the operation is NOT a root

它看起来像你的情况(Socket.BeginReceive),在里面System.Net.Sockets.BaseOverlappedAsyncResult.PinUnmanagedObjects创建了根。

+0

实际上,我的ATcpClient在其构造函数中使用ATcpState,整个事情比我列出的简化代码更复杂。从你的评论中,我认为它是安全的,希望编译器不会那么“聪明”。今晚会做一些测试......谢谢。 – ccppjava 2011-03-07 17:06:10

+0

@ccppjava:您可能只读过我的答案的一部分,请务必查看更新。仅仅引用一个对象不足以防止收集,它必须从“GC根”(例如调用堆栈中的函数中的局部变量,静态成员变量或“GCHandle”)可达。 – 2011-03-07 17:09:15

+0

谢谢你,阅读过你的更新,我认为你是对的。虽然这意味着更复杂的内务管理线程将会运行,但更安全。我同意整个“可能被盘旋”的参考事物可以是整个GC。 – ccppjava 2011-03-07 17:13:58