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

本文关键字:ExecutionContext 删除 Thread 分配 SocketAsyncEventArgs 是否 | 更新日期: 2023-09-27 18:17:44

如果您配置一个使用SocketAsyncEventArgs的简单客户端应用程序,您将注意到ThreadExecutionContext的分配。

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

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

如何避免这些分配?

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

  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);
        }
    }
    

设置isFlowSuppressedtrue的唯一方法,通过StartOperationCommon方法中的条件,是通过调用Setup方法,Setup的唯一调用是在SuppressFlow方法中,你已经讨论过了。

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

实际上,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的跟踪显示内存分配确实下降到零。

相关文章:
  • 没有找到相关文章