使用SocketAsyncEventArgs时是否可以删除ExecutionContext和Thread分配?

时间:2014-08-12 17:04:51

标签: c# multithreading sockets allocation asyncsocket

如果您对使用SocketAsyncEventArgs的简单客户端应用程序进行了分析,您会注意到ThreadExecutionContext分配。

分配的来源是SocketAsyncEventArgs.StartOperationCommon,它使用ExecutionContext.CreateCopy()创建ExecutionContext的副本。

ExecutionContext.SuppressFlow似乎是抑制此分配的好方法。但是,当在新线程中运行时,此方法本身将生成分配。

如何避免这些分配?

2 个答案:

答案 0 :(得分:2)

  1. <强> 的SocketAsyncEventArgs

    public class SocketAsyncEventArgs : EventArgs, IDisposable {
    //...
    // Method called to prepare for a native async socket call.
    // This method performs the tasks common to all socket operations.
    internal void StartOperationCommon(Socket socket) {
    
        //...
    
        // Prepare execution context for callback.
    
        if (ExecutionContext.IsFlowSuppressed()) {    
        // This condition is what you need to pass.
    
            // Fast path for when flow is suppressed.
    
            m_Context = null;
            m_ContextCopy = null;
    
        } else {
    
            // Flow is not suppressed.
    
            //...
    
            // If there is an execution context we need
             //a fresh copy for each completion.
    
            if(m_Context != null) {
                m_ContextCopy = m_Context.CreateCopy();
            }
        }
    
        // Remember current socket.
        m_CurrentSocket = socket;
       }
    
    
    
    
        [Pure]
        public static bool IsFlowSuppressed()
        {
            return  Thread.CurrentThread.GetExecutionContextReader().IsFlowSuppressed;
        }
       //...
        }
    
  2. <强> 的ExecutionContext

    [Serializable] 
    public sealed class ExecutionContext : IDisposable, ISerializable
    {
    //...
    // Misc state variables.
    private ExecutionContext m_Context;
    private ExecutionContext m_ContextCopy;
    private ContextCallback m_ExecutionCallback;
    //...
    
    internal struct Reader
    {
        ExecutionContext m_ec;
        //...
         public bool IsFlowSuppressed 
        {
         #if !FEATURE_CORECLR
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
         #endif
            get { return IsNull ? false : m_ec.isFlowSuppressed; } 
        }
    
      } //end of Reader
    
    
    internal bool isFlowSuppressed 
       { 
        get 
        { 
            return (_flags & Flags.IsFlowSuppressed) != Flags.None; 
        }
        set
        {
            Contract.Assert(!IsPreAllocatedDefault);
            if (value)
                _flags |= Flags.IsFlowSuppressed;
            else
                _flags &= ~Flags.IsFlowSuppressed;
        }
       }
    
    
    [System.Security.SecurityCritical]  // auto-generated_required
    public static AsyncFlowControl SuppressFlow()
    {
        if (IsFlowSuppressed())
        {
            throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotSupressFlowMultipleTimes"));
        }
        Contract.EndContractBlock();
        AsyncFlowControl afc = new AsyncFlowControl();
        afc.Setup();
        return afc;
    }
    //...
    }//end of ExecutionContext.
    
  3. <强> AsyncFlowControl

    public struct AsyncFlowControl: IDisposable
    {
    private bool useEC;
    private ExecutionContext _ec;
    
    //... 
    
    [SecurityCritical]
    internal void Setup()
    {
        useEC = true;
        Thread currentThread = Thread.CurrentThread;
        _ec = currentThread.GetMutableExecutionContext();
        _ec.isFlowSuppressed = true;
        _thread = currentThread;
    }
    }
    
  4. <强>

    // deliberately not [serializable]
    [ClassInterface(ClassInterfaceType.None)]
    [ComDefaultInterface(typeof(_Thread))]
    [System.Runtime.InteropServices.ComVisible(true)]
    public sealed class Thread : CriticalFinalizerObject, _Thread
    {
    
     //...
    
     [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        internal ExecutionContext.Reader GetExecutionContextReader()
        {
            return new ExecutionContext.Reader(m_ExecutionContext);
        }
    }
    
  5. isFlowSuppressed设置为true以便在StartOperationCommon方法中传递条件的only方法是调用Setup方法,对Setup的调用采用SuppressFlow方式,您已经讨论过了。

      

    如您所见,SuppressFlow是唯一的解决方案。

答案 1 :(得分:0)

实际上,SuppressFlow没有分配。它返回AsyncFlowControl,即struct。适当的解决方案基本上是如下调用SendAsyncReceiveAsync

public static bool SendAsyncSuppressFlow(this Socket self, SocketAsyncEventArgs e)
{
    var control = ExecutionContext.SuppressFlow();
    try
    {
        return self.SendAsync(e);
    }
    finally
    {
        control.Undo();
    }
}

public static bool ReceiveAsyncSuppressFlow(this Socket self, SocketAsyncEventArgs e)
{
    var control = ExecutionContext.SuppressFlow();
    try
    {
        return self.ReceiveAsync(e);
    }
    finally
    {
        control.Undo();
    }
}

我创建了这些扩展方法,以使其更加简单明了。

dotMemory的痕迹表明,内存分配确实减少到了零。