Подтвердить что ты не робот

Можно ли удалить выделение ExecutionContext и Thread при использовании SocketAsyncEventArgs?

Если вы профилируете простое клиентское приложение, использующее SocketAsyncEventArgs, вы заметите Thread и ExecutionContext распределения.

Источником распределений является SocketAsyncEventArgs.StartOperationCommon, который создает копию ExecutionContext с ExecutionContext.CreateCopy().

ExecutionContext.SuppressFlow кажется хорошим способом подавить это распределение. Однако сам этот метод будет генерировать распределения при запуске в новом потоке.

Как я могу избежать этих распределений?

4b9b3361

Ответ 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;
        }
       //...
        }
    
  • 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.
    
  • 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;
    }
    }
    
  • Тема

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

только способ установить isFlowSuppressed в true, чтобы передать условие в методе StartOperationCommon, вызвав Setup, и единственный вызов Setup находится в методе SuppressFlow, о котором вы говорили.

Как вы можете видеть, SuppressFlow - единственное решение.

Ответ 2

На самом деле, SuppressFlow не выделяет. Возвращает AsyncFlowControl, который является struct. Правильное решение в основном состоит в том, чтобы вызвать SendAsync и ReceiveAsync следующим образом:

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 показали, что распределение памяти действительно уменьшается до нуля.