调用基于异步任务的WCF方法是否利用I / O完成端口或线程池线程来调用延续?

我有以下WCF合同:

[ServiceContract(Namespace = "http://abc/Services/AdminService")] public interface IAdminService { [OperationContract] string GetServiceVersion(); // More methods here } 

GetServiceVersion是一个返回一些字符串的简单方法。 它用作ping来检查服务是否可访问。

现在我想异步调用它,认为它比使用.NET线程在后台调用它更有效。

所以,我为此提出了以下接口:

 [ServiceContract(Namespace = "http://abc/Services/AdminService")] public interface IMiniAdminService { [OperationContract(Action = "http://abc/Services/AdminService/IAdminService/GetServiceVersion", ReplyAction = "http://abc/Services/AdminService/IAdminService/GetServiceVersionResponse")] Task GetServiceVersionAsync(); } 

这使得可以异步调用GetServiceVersion API:

 var tmp = new ChannelFactory("AdminServiceClientEndpoint"); var channelFactory = new ChannelFactory(tmp.Endpoint.Binding, tmp.Endpoint.Address); var miniAdminService = channelFactory.CreateChannel(); return miniAdminService.GetServiceVersionAsync().ContinueWith(t => { if (t.Exception != null) { // The Admin Service seems to be unavailable } else { // The Admin Service is available } }); 

代码有效。

我的问题是 – 它是否利用IOCP来调用延续?

通常,有没有办法知道是否通过IOCP调用延续(在调试器中,如果需要)?

PS

这是我的异步WCF方法延续的堆栈跟踪:

 > *** My Code *** Line 195 C# mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromResultTask.InnerInvoke() + 0x111 bytes mscorlib.dll!System.Threading.Tasks.Task.Execute() + 0x69 bytes mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) + 0x4f bytes mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x28d bytes mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x47 bytes mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) + 0x3b5 bytes mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) + 0x104 bytes mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() + 0x2a bytes mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() + 0x249 bytes mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() + 0x1e bytes [Native to Managed Transition] 

现在,这个堆栈跟踪看起来非常类似于我从Task.Factory.StartNew调用的方法,它实际上是基于线程池的:

 > *** My Code *** Line 35 C# mscorlib.dll!System.Threading.Tasks.Task.InnerInvoke() + 0x59 bytes mscorlib.dll!System.Threading.Tasks.Task.Execute() + 0x60 bytes mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) + 0x37 bytes mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x1a2 bytes mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x33 bytes mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) + 0x2ff bytes mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) + 0xd3 bytes mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() + 0x22 bytes mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() + 0x22e bytes mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() + 0x18 bytes [Native to Managed Transition] 

首先,您需要添加TaskContinuationOptions.ExecuteSynchronously ,以确保在异步IO操作已完成的同一线程上调用继续回调:

 return miniAdminService.GetServiceVersionAsync().ContinueWith(t => { if (t.Exception != null) { // The Admin Service seems to be unavailable } else { // The Admin Service is available } }, TaskContinuationOptions.ExecuteSynchronously); 

显然,.NET中没有API来判断该线程是否是IOCP池线程。 您只能判断该线程是否是线程池线程( Thread.CurrentThread.IsThreadPoolThread ),对于IOCP线程也是如此。

在Win32中,使用CreateIoCompletionPort API创建IOCP线程池,但我找不到Win32 API来检查线程是否属于此类池。

因此,这是一个有点人为的例子,在实践中检验这个理论,使用HtppClient作为测试工具。 首先,我们确保所有非IOCP线程都使用-1填充了ThreadStatic变量s_mark 。 然后我们启动IO绑定操作并检查线程上的s_mark ,其中IO绑定操作完成:

 using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace ConsoleApplication_22465346 { public class Program { [ThreadStatic] static volatile int s_mark; // Main public static void Main(string[] args) { const int THREADS = 50; // init the thread pool ThreadPool.SetMaxThreads( workerThreads: THREADS, completionPortThreads: THREADS); ThreadPool.SetMinThreads( workerThreads: THREADS, completionPortThreads: THREADS); // populate s_max for non-IOCP threads for (int i = 0; i < THREADS; i++) { ThreadPool.QueueUserWorkItem(_ => { s_mark = -1; Thread.Sleep(1000); }); } Thread.Sleep(2000); // non-IOCP test Task.Run(() => { // by now all non-IOCP threads have s_mark == -1 Console.WriteLine("Task.Run, s_mark: " + s_mark); Console.WriteLine("IsThreadPoolThread: " + Thread.CurrentThread.IsThreadPoolThread); }).Wait(); // IOCP test var httpClient = new HttpClient(); httpClient.GetStringAsync("http://example.com").ContinueWith(t => { // all IOCP threads have s_mark == 0 Console.WriteLine("GetStringAsync.ContinueWith, s_mark: " + s_mark); Console.WriteLine("IsThreadPoolThread: " + Thread.CurrentThread.IsThreadPoolThread); }, TaskContinuationOptions.ExecuteSynchronously).Wait(); Console.WriteLine("Enter to exit..."); Console.ReadLine(); } } } 

输出:

 Task.Run,​​s_mark:-1
 IsThreadPoolThread:是的
 GetStringAsync.ContinueWith,s_mark:0
 IsThreadPoolThread:是的
进入退出...

我认为这可能足以证实在IOCP线程发生IO绑定延续的理论。

一个很好的阅读,相关:Stephen Cleary的“没有线索” 。