当使用SocketAsyncEventArgs时,是否可以删除ExecutionContext和Thread分配?
本文关键字:ExecutionContext 删除 Thread 分配 SocketAsyncEventArgs 是否 | 更新日期: 2023-09-27 18:17:44
如果您配置一个使用SocketAsyncEventArgs
的简单客户端应用程序,您将注意到Thread
和ExecutionContext
的分配。
分配的来源是SocketAsyncEventArgs.StartOperationCommon
,它与ExecutionContext.CreateCopy()
一起创建ExecutionContext的副本。
ExecutionContext.SuppressFlow
似乎是抑制这种分配的好方法。但是,当在新线程中运行时,此方法本身将生成分配。
如何避免这些分配?
-
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
是唯一的解决方案。
实际上,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的跟踪显示内存分配确实下降到零。