mar*_*ark 8 c# wcf asynchronous task-parallel-library
我有以下WCF合同:
[ServiceContract(Namespace = "http://abc/Services/AdminService")]
public interface IAdminService
{
[OperationContract]
string GetServiceVersion();
// More methods here
}
Run Code Online (Sandbox Code Playgroud)
这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<string> GetServiceVersionAsync();
}
Run Code Online (Sandbox Code Playgroud)
这使得可以GetServiceVersion
异步调用API:
var tmp = new ChannelFactory<IAdminService>("AdminServiceClientEndpoint");
var channelFactory = new ChannelFactory<IMiniAdminService>(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
}
});
Run Code Online (Sandbox Code Playgroud)
代码有效.
我的问题是 - 它是否利用IOCP来调用延续?
通常,有没有办法知道是否通过IOCP调用延续(在调试器中,如果需要)?
PS
这是我的异步WCF方法延续的堆栈跟踪:
> *** My Code *** Line 195 C#
mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromResultTask<string>.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]
Run Code Online (Sandbox Code Playgroud)
现在,这个堆栈跟踪看起来非常类似于我从一个名为from的方法获得的跟踪Task.Factory.StartNew
,这实际上是基于线程池的:
> *** My Code *** Line 35 C#
mscorlib.dll!System.Threading.Tasks.Task<int>.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]
Run Code Online (Sandbox Code Playgroud)
首先,您需要添加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);
Run Code Online (Sandbox Code Playgroud)
显然,.NET中没有API来判断该线程是否是IOCP池线程.您只能判断该线程是否是线程池线程(Thread.CurrentThread.IsThreadPoolThread
),它也true
适用于IOCP线程.
在Win32中,使用CreateIoCompletionPort
API 创建IOCP线程池,但是我找不到Win32 API来检查线程是否属于这样的池.
因此,这是一个有点人为的例子,在实践中检验这个理论,HtppClient
用作测试工具.首先,我们要确保所有非IOCP线程都填充ThreadStatic
变量s_mark
用-1
.然后我们启动一个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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
Task.Run, s_mark: -1 IsThreadPoolThread: True GetStringAsync.ContinueWith, s_mark: 0 IsThreadPoolThread: True Enter to exit...
我认为这可能足以证实在IOCP线程上发生IO绑定延续的理论.
一个很好的阅读,相关:Stephen Cleary的"没有线索".