探讨 : Host在IIS上的WCF Service的执行方式

一个WCF请求由两个线程来完成

运行在IIS上的WCF service, 你可能会注意到一个比较有趣的现象. 当WCF service接收到一个请求时, 这个请求实际上会有两个线程在执行这个请求.

  • 一个线程是来自于CLR的ThreadPool的线程. 这是一个Worker Thread用于接收.svc页面的访问请求.
  • 另外一个线程是 I/O 线程, 用于执行WCF的逻辑.

你可以参考下面的实例代码. 该代码用于模拟一个WCF执行时间比较长的场景.

[ServiceContract]
public interface IService1
{
    [OperationContract]
    string GetData(int value);
}

public class Service1 : IService1
{
    public string GetData(int value)
    {
        System.Threading.Thread.Sleep(value * 1000);

        return string.Format("You slept : {0} seconds", value);
    }

}

如果是.NET Framework 3.5, 您会在运行时看到类似的CALL STACK. 线程26 正在执行一个Service1.svc的请求. 整个请求正是一个WCF的请求. 从CALL STACK上看, 这个线程已经被挂起, 并且等待一个异步线程的返回.

通过!threads命令, 可以验证这个线程是一个Worker Thread. 在这一点上, .svc页面的请求与.aspx页面的请求, 并没有太多的区别.

0:026> !psscor2.aspxpages
Going to dump the HttpContexts found in the heap.
HttpContext    Timeout  Completed     Running  ThreadId ReturnCode   Verb RequestPath+QueryString
0x00000006101094a0    110Sec        no         3 Sec      26        200   POST /Service1.svc
Total 5 HttpContext objects

0:026> !clrstack
OS Thread Id: 0x12a0 (26)
Child-SP         RetAddr          Call Site
00000006931be540 00007ff97510422b System.Threading.WaitHandle.WaitOne(Int64, Boolean)
00000006931be580 00007ff975100c6c System.ServiceModel.Activation.HostedHttpRequestAsyncResult.ExecuteSynchronous(System.Web.HttpApplication, Boolean)
00000006931be610 00007ff9bd458b90 System.ServiceModel.Activation.HttpModule.ProcessRequest(System.Object, System.EventArgs)
00000006931be660 00007ff9bd4497db System.Web.HttpApplication+SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
00000006931be6c0 00007ff9bdb3ffa1 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
00000006931be760 00007ff9bdb304f2 System.Web.HttpApplication+PipelineStepManager.ResumeSteps(System.Exception)
00000006931be8f0 00007ff9bdb11e79 System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext, System.AsyncCallback)
00000006931be940 00007ff9bdc42f61 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest, System.Web.HttpContext)
00000006931bea60 00007ff9bdc42b2b System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)
00000006931bebe0 00007ff9bdc42884 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)
00000006931bec40 00007ff9d48f8b9a DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32)
00000006931bf4a0 00007ff9bdc43090 DomainNeutralILStubClass.IL_STUB(IntPtr, System.Web.RequestNotificationStatus ByRef)
00000006931bf580 00007ff9bdc42b2b System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)
00000006931bf700 00007ff9bdc42884 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)
00000006931bf760 00007ff9d48f8deb DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32)

0:029> !threads
ThreadCount: 9
UnstartedThread: 0
BackgroundThread: 9
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
                                              PreEmptive                                                Lock
       ID OSID        ThreadOBJ     State   GC     GC Alloc Context                  Domain           Count APT Exception
   7    1 1d00 000000054eb974b0      8220 Enabled  0000000550122328:0000000550123008 000000054eb8f200     0 Ukn
  22    2 5dcc 000000054eba9620      b220 Enabled  0000000000000000:0000000000000000 000000054eb8f200     0 MTA (Finalizer)
  24    3 4234 00000006914a4f00    80a220 Enabled  0000000000000000:0000000000000000 000000054eb8f200     0 MTA (Threadpool Completion Port)
  25    4 500c 00000006914a6560      1220 Enabled  0000000000000000:0000000000000000 000000054eb8f200     0 Ukn
  26    5 12a0 000000069151ee20   380b220 Enabled  000000061010a9e8:000000061010afd0 00000006914a7010     1 MTA (Threadpool Worker)

这个请求正是  I/O 线程来执行, 从Managed的Call Stack上可以看出来是执行了上面实例代码的Service1.GetData的方法.

在Native的Callstack中, mscorwks!ThreadpoolMgr::CompletionPortThreadStart 说明了这里开始的是一个I/O线程. 通过 !thread 命令, 同样也可以验证出这个是I/O线程.

0:029> !clrstack
OS Thread Id: 0x5894 (29)
Child-SP         RetAddr          Call Site
00000006941ce740 00007ff97570022c WCFSite.Service1.GetData(Int32)
00000006941ce790 00007ff975751329 DynamicClass.SyncInvokeGetData(System.Object, System.Object[], System.Object[])
00000006941ce7d0 00007ff9754ab99c System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(System.Object, System.Object[], System.Object[] ByRef)
00000006941ce920 00007ff9754ab092 System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(System.ServiceModel.Dispatcher.MessageRpc ByRef)
00000006941ce9f0 00007ff9754aac57 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(System.ServiceModel.Dispatcher.MessageRpc ByRef)
00000006941cea80 00007ff9754a91b3 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(System.ServiceModel.Dispatcher.MessageRpc ByRef)
00000006941ceb00 00007ff9754a82e3 System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean)
00000006941cebc0 00007ff9754a588f System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext, Boolean, System.ServiceModel.OperationContext)
00000006941cef10 00007ff9754a51d7 System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext, System.ServiceModel.OperationContext)
00000006941cef80 00007ff975105bad System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult)
00000006941cefd0 00007ff9d3849789 System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper+WorkItem.Invoke2()
00000006941cf050 00007ff975105b30 System.Security.SecurityContext.Run(System.Security.SecurityContext, System.Threading.ContextCallback, System.Object)
00000006941cf090 00007ff975105a0c System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper+WorkItem.Invoke()
00000006941cf0e0 00007ff975105842 System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper.ProcessCallbacks()
00000006941cf150 00007ff9751057b1 System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper.CompletionCallback(System.Object)
00000006941cf190 00007ff975105705 System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper+ScheduledOverlapped.IOCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
00000006941cf1c0 00007ff9d381a85e System.ServiceModel.Diagnostics.Utility+IOCompletionThunk.UnhandledExceptionFrame(UInt32, UInt32, System.Threading.NativeOverlapped*)
00000006941cf220 00007ff9d48fb022 System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)

0:029> kL
Child-SP          RetAddr           Call Site
00000006`941ce408 00007ff9`ec76124a ntdll!ZwDelayExecution+0xa
00000006`941ce410 00007ff9`d4b6feed KERNELBASE!SleepEx+0xa2
00000006`941ce4b0 00007ff9`d474a585 mscorwks!EESleepEx+0x2d
00000006`941ce530 00007ff9`d4d2e2d9 mscorwks!Thread::UserSleep+0x71
00000006`941ce590 00007ff9`757101ab mscorwks!ThreadNative::Sleep+0xf9
00000006`941ce740 00007ff9`7570022c WCFSite!WCFSite.Service1.GetData(Int32)+0x3b
00000006`941ce790 00007ff9`75751329 System_ServiceModel!DynamicClass.SyncInvokeGetData(System.Object, System.Object[], System.Object[])+0x5c
00000006`941ce7d0 00007ff9`754ab99c System_ServiceModel!System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(System.Object, System.Object[], System.Object[] ByRef)+0x3a9
00000006`941ce920 00007ff9`754ab092 System_ServiceModel!System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(System.ServiceModel.Dispatcher.MessageRpc ByRef)+0x12c
00000006`941ce9f0 00007ff9`754aac57 System_ServiceModel!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(System.ServiceModel.Dispatcher.MessageRpc ByRef)+0x162
00000006`941cea80 00007ff9`754a91b3 System_ServiceModel!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(System.ServiceModel.Dispatcher.MessageRpc ByRef)+0x77
00000006`941ceb00 00007ff9`754a82e3 System_ServiceModel!System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean)+0x103
00000006`941cebc0 00007ff9`754a588f System_ServiceModel!System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext, Boolean, System.ServiceModel.OperationContext)+0x4f3
00000006`941cef10 00007ff9`754a51d7 System_ServiceModel!System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext, System.ServiceModel.OperationContext)+0x18f
00000006`941cef80 00007ff9`75105bad System_ServiceModel!System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult)+0x47
00000006`941cefd0 00007ff9`d3849789 System_ServiceModel!System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper+WorkItem.Invoke2()+0x3d
00000006`941cf050 00007ff9`75105b30 mscorlib_ni!System.Security.SecurityContext.Run(System.Security.SecurityContext, System.Threading.ContextCallback, System.Object)+0x69
00000006`941cf090 00007ff9`75105a0c System_ServiceModel!System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper+WorkItem.Invoke()+0x50
00000006`941cf0e0 00007ff9`75105842 System_ServiceModel!System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper.ProcessCallbacks()+0x16c
00000006`941cf150 00007ff9`751057b1 System_ServiceModel!System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper.CompletionCallback(System.Object)+0x62
00000006`941cf190 00007ff9`75105705 System_ServiceModel!System.ServiceModel.Channels.IOThreadScheduler+CriticalHelper+ScheduledOverlapped.IOCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)+0x11
00000006`941cf1c0 00007ff9`d381a85e SMDiagnostics!System.ServiceModel.Diagnostics.Utility+IOCompletionThunk.UnhandledExceptionFrame(UInt32, UInt32, System.Threading.NativeOverlapped*)+0x45
00000006`941cf220 00007ff9`d48fb022 mscorlib_ni!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)+0xce
00000006`941cf270 00007ff9`d4797bc3 mscorwks!CallDescrWorker+0x82
00000006`941cf2d0 00007ff9`d4797a82 mscorwks!CallDescrWorkerWithHandler+0xd3
00000006`941cf370 00007ff9`d4760b3b mscorwks!DispatchCallDebuggerWrapper+0x3e
00000006`941cf3d0 00007ff9`d4cc86a3 mscorwks!DispatchCallNoEH+0x5f
00000006`941cf450 00007ff9`d473a768 mscorwks!BindIoCompletionCallBack_Worker+0xb3
00000006`941cf510 00007ff9`d476c2c5 mscorwks!ManagedThreadBase_DispatchInner+0x2c
00000006`941cf560 00007ff9`d48556a1 mscorwks!ManagedThreadBase_DispatchMiddle+0x9d
00000006`941cf630 00007ff9`d472cb89 mscorwks!ManagedThreadBase_DispatchOuter+0x31
00000006`941cf670 00007ff9`d473a405 mscorwks!ManagedThreadBase_DispatchInCorrectAD+0x15
00000006`941cf6a0 00007ff9`d473a78d mscorwks!Thread::DoADCallBack+0x145
00000006`941cf810 00007ff9`d476c2c5 mscorwks!ManagedThreadBase_DispatchInner+0x51
00000006`941cf860 00007ff9`d48556a1 mscorwks!ManagedThreadBase_DispatchMiddle+0x9d
00000006`941cf930 00007ff9`d48b8c11 mscorwks!ManagedThreadBase_DispatchOuter+0x31
00000006`941cf970 00007ff9`d4de135a mscorwks!ManagedThreadBase_FullTransitionWithAD+0x35
00000006`941cf9d0 00007ff9`d4de1533 mscorwks!BindIoCompletionCallbackStubEx+0xca
00000006`941cfa80 00007ff9`d471bc0e mscorwks!BindIoCompletionCallbackStub+0x13
00000006`941cfab0 00007ff9`d4737f74 mscorwks!ThreadpoolMgr::CompletionPortThreadStart+0x186
00000006`941cfb50 00007ff9`eecb16ad mscorwks!Thread::intermediateThreadProc+0x78
00000006`941cfe20 00007ff9`ef294629 kernel32!BaseThreadInitThunk+0xd
00000006`941cfe50 00000000`00000000 ntdll!RtlUserThreadStart+0x1d

0:029> !threads
ThreadCount: 9
UnstartedThread: 0
BackgroundThread: 9
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
                                              PreEmptive                                                Lock
       ID OSID        ThreadOBJ     State   GC     GC Alloc Context                  Domain           Count APT Exception
  29    8 5894 0000000693d8c760   a80b220 Enabled  00000005d031b410:00000005d031cfd0 00000006914a7010     0 MTA (Threadpool Completion Port)

异步请求的执行

非常不同于原先Web Page和Web Servcie的执行方式. Web Page和Web Servcie均由ASP.NET的Worker Thread来执行请求. WCF最为显著的特点即是异步执行.

这一优势体现在 : WCF应用有更高的灵活性. 能够同一时间平行的执行不同的操作请求. 在WCF操作执行完成之后, 将CALLBACK到之前用于handle页面请求的线程上. 并由这个线程(WorkerThread)来进行返回. 从而提供了WCF服务的吞吐量.

传统的Web Page和Web Service, 当终端客户发起一个请求的时候, 总是会期待服务器端返回一个结果. 因此, 如果发起的请求在服务器端遇到了一些瓶颈, 造成长时间的执行. 客户端也必然需要处于等待状态.

在这种WCF的执行模式下, WCF客户端可以运行在Fire And Forget的模式下. 在有些情况下, 客户端只希望向服务器端发送一个执行请求, 但是并不在乎服务器端执行的结果, 因此也没有必要等待服务器端的返回. 只是的将执行请求提交到WCF服务端, 剩下的事情由WCF服务端来完成. 也不希望收到WCF服务端执行时间长短的影响. 如果有这种需要, 可以运行在fire and forget的模式下. 页面请求在Worker Thread接收后, 按照正常的步骤异步调用I/O线程并且执行WCF的操作.于此同时Worker Thread 可以立即返回响应客户端.

请参考 :

What You Need To Know About One-Way Calls, Callbacks, And Events 

Need sample fire and forget async call to WCF service

WCF 4.5上的改进

上面已经讨论了很多WCF请求是如何又两个线程来执行的, 以及这种方式的优点. 然而, 这种方式在.NET FRAMEWROK 3.5上依然有一些缺陷.

按照上面的示例. 当请求到达WCF Servcie后, 服务端会启动两个线程. 一个Worker Thread和一个 I/O Completion Port. Worker Thread用于接收.svc页面的请求, 并且等待IOCP的执行完成然后返回到客户端. IOCP则运用WCF的操作. 在代码中, WCF的操作会sleep一段时间, 用于模拟长时间的操作. 在这种情况下, 我们可以看到Work Thread是一直处于等待状态.

这种情况下会产生一个问题. 如果WCF的操作都非常耗时, 而且此时业务非常繁忙. 进程的线程池有可能很快就会耗尽. 而且这些线程看起来无所事事, 绝大多数都在等待WCF操作的返回而已. 这显然造成了一种浪费. 在提高异步执行能力的同时, 增加的线程已经虚拟内存的消耗. 即使不在执行任何工作, 线程本身也需要使用一定的虚内内存.

这一问题在.NET FRAMWORK 4.5中得到了一定的改进.

当请求到来时, 同样由两个线程来提供服务. 一个线程是来自于CLR的ThreadPool的线程. 这是一个Worker Thread用于接收.svc页面的访问请求. 另外一个线程是 I/O 线程, 用于执行WCF的逻辑. 当请求递交给IOCP之后, Worker Thread并没有一直处于等待状态, 而将已经创建好的HttpContext固定在适当的位置. 它会PINNED在一个HandleTable (strong handle). 以确保这个HttpContext不会被意外的回收. 然后 这个Worker Thread会回到线程池等待分配下一个工作.

在IOCP完成工作需要将结果返回给客户端的时候, 线程池会调度一个线程将对应的HttContext重新拾起, 将需要返回的结果返回给客户端.

下面的内容是.NET FRAMWORK 4.5上的运行时态的示例.

执行WCF请求的线程并没有太多的变化. 从Managed的CALL STACK上看, 它依然是一个IOCP, 并且最后Call到了我们的示例代码中进行了休眠.

主要的区别在于, 我们虽然能找到与之相关的HttpContext, 然后这个HttpContext当前并没有执行在任何一个线程上.在完成IOCP的调用后, 该Worker Thread已经返回了线程池.

0:015> !clrstack -n
OS Thread Id: 0x4278 (15)
        Child SP               IP Call Site
0000005eeee7e098 00007ff9ef2baeca [HelperMethodFrame: 0000005eeee7e098] System.Threading.Thread.SleepInternal(Int32)
0000005eeee7e190 00007ff9e025f399 System.Threading.Thread.Sleep(Int32)
0000005eeee7e1c0 00007ff981ef011b WCFSite.Service1.GetData(Int32)
0000005eeee7e210 00007ff981ee0207 DynamicClass.SyncInvokeGetData(System.Object, System.Object[], System.Object[])
0000005eeee7e250 00007ff9d731f2e2 System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(System.Object, System.Object[], System.Object[] ByRef)
0000005eeee7e430 00007ff9d731b825 System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(System.ServiceModel.Dispatcher.MessageRpc ByRef)
0000005eeee7e7e0 00007ff9d731b020 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(System.ServiceModel.Dispatcher.MessageRpc ByRef)
0000005eeee7e890 00007ff9d731a70b System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(System.ServiceModel.Dispatcher.MessageRpc ByRef)
0000005eeee7ea70 00007ff9d731911e System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean)
0000005eeee7eb30 00007ff9d731866e System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext, Boolean, System.ServiceModel.OperationContext)
0000005eeee7ef10 00007ff9d73168b2 System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext, System.ServiceModel.OperationContext)
0000005eeee7ef90 00007ff9d73163fb System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult)
0000005eeee7eff0 00007ff9d6c3c001 System.Runtime.IOThreadScheduler+ScheduledOverlapped.IOCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
0000005eeee7f050 00007ff9d6c3bf80 System.Runtime.Fx+IOCompletionThunk.UnhandledExceptionFrame(UInt32, UInt32, System.Threading.NativeOverlapped*)
0000005eeee7f0b0 00007ff9e0275db6 System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
0000005eeee7f268 00007ff9e1430453 [GCFrame: 0000005eeee7f268]
0000005eeee7f438 00007ff9e1430453 [DebuggerU2MCatchHandlerFrame: 0000005eeee7f438]
0000005eeee7f5d8 00007ff9e1430453 [ContextTransitionFrame: 0000005eeee7f5d8]
0000005eeee7f7f8 00007ff9e1430453 [DebuggerU2MCatchHandlerFrame: 0000005eeee7f7f8]

0:015> !aspxpagesext
Address          Completed Timeout Time (secs) ThreadId ReturnCode Verb Url
================ ========= ======= =========== ======== ========== ==== =============
0000005eef0540d8        no   11647           7      NO THREAD ID 200 POST /Service1.svc

HandleTable:
    0000005eee5913c0 (strong handle)
    -> 00000061ef0a0460 System.Threading.Thread
    -> 00000061ef031d60 System.Runtime.Remoting.Contexts.Context
    -> 00000061ef031a28 System.AppDomain
    -> 00000060eef67ac0 System.AssemblyLoadEventHandler
    -> 00000060eef679e8 System.Web.Compilation.MemoryBuildResultCache
    -> 00000061ef094cc8 System.Web.Caching.CacheMultiple
    -> 00000061ef0948f8 System.Web.Caching.CacheCommon
    -> 00000061ef09aa10 System.Threading.Timer
    -> 00000061ef09aa88 System.Threading.TimerHolder
    -> 00000061ef09aa30 System.Threading.TimerQueueTimer
    -> 00000061ef08ddb0 System.Threading.TimerQueueTimer
    -> 00000061ef08dd50 System.Threading.TimerCallback
    -> 00000061ef08da88 System.Web.RequestTimeoutManager
    -> 00000061ef08dac0 System.Object[]
    -> 00000061ef08dbc0 System.Web.Util.DoubleLinkList
    -> 0000005eef0556d8 System.Web.RequestTimeoutManager+RequestTimeoutEntry
    -> 0000005eef0540d8 System.Web.HttpContext

    0000005eeeb115e8 (strong handle)
    -> 0000005eef0545f0 System.Web.RootedObjects
    -> 0000005eef0540d8 System.Web.HttpContext

总结

运行在IIS上的WCF service, 请求实际上会有两个线程在执行这个请求.

  • 一个线程是来自于CLR的ThreadPool的线程. 这是一个Worker Thread用于接收.svc页面的访问请求.
  • 另外一个线程是 I/O 线程, 用于执行WCF的逻辑.

这种模式提高了WCF服务的吞吐量, 也使得WCF具有更高的灵活性.

在.NET FRAMWORK 3.5中, Worker Thread会被挂起, 并且一直等待IOCP的返回. 这中模式造成了Worker Thread的资源浪费. 在 4.5上, 这一模式得到了改进. Worker Thread在调用IOCP之后会回到线程池. 在必要的时候线程池会重新分配线程进行下一步的操作.

参考资料 :

 WCF Request Throttling and Server Scalability

WCF service may scale up slowly under load

Synchronization Contexts in WCF

What You Need To Know About One-Way Calls, Callbacks, And Events 

Need sample fire and forget async call to WCF service

原文地址:https://www.cnblogs.com/developersupport/p/wcf-service-execution-model.html