并调用异步任务基于WCF方法,利用I / O完成端口或一个线程池线程调用的延续? [英] Does calling asynchronous Task based WCF method utilize the I/O completion port or a Thread Pool thread to call the continuation?
问题描述
我有以下的WCF合同:
[的ServiceContract(命名空间=HTTP:// ABC /服务/ AdminService)]
公共接口IAdminService
{
[OperationContract的]
串GetServiceVersion(); //这里更多的方法
}
的 GetServiceVersion
是一个简单的方法返回一些字符串。它是用来作为一个ping检查服务是否可达。
现在我想异步调用它,认为它会比使用.NET线程中调用它的背景更加高效。
所以,我想出了以下接口仅仅用于这一目的:
[的ServiceContract(命名空间=HTTP:// ABC /服务/ AdminService)]
公共接口IMiniAdminService
{
[OperationContract的(动作=HTTP:// ABC /服务/ AdminService / IAdminService / GetServiceVersion,ReplyAction =HTTP:// ABC /服务/ AdminService / IAdminService / GetServiceVersionResponse)]
任务<串GT; GetServiceVersionAsync();
}
这使得它可以异步调用的 GetServiceVersion
API
VAR TMP =新的ChannelFactory< IAdminService>(AdminServiceClientEndpoint);
VAR的ChannelFactory =新的ChannelFactory< IMiniAdminService>(tmp.Endpoint.Binding,tmp.Endpoint.Address);
变种miniAdminService = channelFactory.CreateChannel();
返回miniAdminService.GetServiceVersionAsync()ContinueWith(T =>
{
如果(t.Exception!= NULL)
{
//管理服务似乎是不可用
}
其他
{
//管理服务可
}
});
在code工作。
我的问题是 - 它利用IOCP调用的延续?
在一般情况下,有没有办法知道的延续是否通过调用IOCP否(在调试器,如果需要的话)?
P.S。
下面是我的异步方法的WCF延续的堆栈跟踪:
> ***我的code ***线195 C#
!mscorlib.dll中System.Threading.Tasks.ContinuationTaskFromResultTask<串GT; .InnerInvoke()+ 0x111字节
mscorlib.dll中!System.Threading.Tasks.Task.Execute()+ 0×69字节
mscorlib.dll中!System.Threading.Tasks.Task.ExecutionContextCallback(obj对象)+ 0x4f字节
mscorlib.dll中!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext的ExecutionContext,System.Threading.ContextCallback回调,对象状态,布尔preserveSyncCtx)+ 0x28d字节
mscorlib.dll中!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext的ExecutionContext,System.Threading.ContextCallback回调,对象状态,布尔preserveSyncCtx)+ 0X47字节
mscorlib.dll中!System.Threading.Tasks.Task.ExecuteWithThreadLocal(REF System.Threading.Tasks.Task currentTaskSlot)+ 0x3b5字节
mscorlib.dll中!System.Threading.Tasks.Task.ExecuteEntry(布尔b preventDoubleExecution)+量0x104字节
mscorlib.dll中!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()+ 0x2a字节
mscorlib.dll中!System.Threading.ThreadPoolWorkQueue.Dispatch()+ 0x249字节
mscorlib.dll中!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()+ 0X1E字节
[原产于托管过渡]
现在,该堆栈跟踪看起来非常相似,一个我得到一个从 Task.Factory.StartNew
调用的方法,这确实是线程池的基础:
> ***我的code *** 35号线C#
!mscorlib.dll中System.Threading.Tasks.Task< INT> .InnerInvoke()+ 0x59字节
mscorlib.dll中!System.Threading.Tasks.Task.Execute()+ 0x60的字节
mscorlib.dll中!System.Threading.Tasks.Task.ExecutionContextCallback(obj对象)+ 0x37符号字节
mscorlib.dll中!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext的ExecutionContext,System.Threading.ContextCallback回调,对象状态,布尔preserveSyncCtx)+ 0x1a2字节
mscorlib.dll中!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext的ExecutionContext,System.Threading.ContextCallback回调,对象状态,布尔preserveSyncCtx)+ 0x33字节
MSCORLIB.DLL!System.Threading.Tasks.Task.ExecuteWithThreadLocal(REF System.Threading.Tasks.Task currentTaskSlot)+ 0x2ff字节
mscorlib.dll中!System.Threading.Tasks.Task.ExecuteEntry(布尔b preventDoubleExecution)+ 0xd3字节
mscorlib.dll中!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()+ 0x22字节
mscorlib.dll中!System.Threading.ThreadPoolWorkQueue.Dispatch()+ 0x22e字节
mscorlib.dll中!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()+为0x18字节
[原产于托管过渡]
首先,你需要添加 TaskContinuationOptions.ExecuteSynchronously
,以确保延续回调被调用在同一个线程异步IO操作已经敲定:
返回miniAdminService.GetServiceVersionAsync()ContinueWith(T =>
{
如果(t.Exception!= NULL)
{
//管理服务似乎是不可用
}
其他
{
//管理服务可
}
},TaskContinuationOptions.ExecuteSynchronously);
显然,没有在.NET API没有告诉如果该线程是一个IOCP池线程。您只能告诉如果线程是一个线程池线程( Thread.CurrentThread.IsThreadPoolThread
),这是真正
的IOCP线程了。
在Win32中,一个IOCP线程池与<创建的href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/aa363862%28v=vs.85%29.aspx\"><$c$c>CreateIoCompletionPort$c$c> API,但我无法找到一个Win32 API的检查,如果线程属于此类池,或者。
所以,这里是有点做作的例子,检查在实践这一理论,使用 HtppClient
作为测试工具。首先,我们要确保所有的非IOCP线程都填充 ThreadStatic
变量 s_mark
与 - 1
。然后,我们启动一个IO绑定操作,并检查 s_mark
上线所在的IO绑定操作完成后得到:
使用系统;
使用System.Net.Http;
使用的System.Threading;
使用System.Threading.Tasks;命名空间ConsoleApplication_22465346
{
公共类节目
{
[ThreadStatic]
静态挥发INT s_mark; //主
公共静态无效的主要(字串[] args)
{
const int的THREADS = 50; //初始化线程池
ThreadPool.SetMaxThreads(
workerThreads:线程,completionPortThreads:线程);
ThreadPool.SetMinThreads(
workerThreads:线程,completionPortThreads:线程); //填充非IOCP线程s_max
的for(int i = 0; I&LT;螺纹;我++)
{
ThreadPool.QueueUserWorkItem(_ =&GT;
{
s_mark = -1;
Thread.sleep代码(1000);
});
}
Thread.sleep代码(2000); //非IOCP测试
Task.Run(()=&GT;
{
//现在所有非IOCP线程具有s_mark == -1
Console.WriteLine(Task.Run,s_mark:+ s_mark);
Console.WriteLine(IsThreadPoolThread:+ Thread.CurrentThread.IsThreadPoolThread);
})。等待(); // IOCP测试
VAR的HttpClient =新的HttpClient();
httpClient.GetStringAsync(http://example.com).ContinueWith(T =&GT;
{
//所有IOCP线程具有s_mark == 0
Console.WriteLine(GetStringAsync.ContinueWith,s_mark:+ s_mark);
Console.WriteLine(IsThreadPoolThread:+ Thread.CurrentThread.IsThreadPoolThread);
},TaskContinuationOptions.ExecuteSynchronously).Wait(); Console.WriteLine(Enter退出...);
到Console.ReadLine();
}
}
}
输出:
Task.Run,s_mark:-1
IsThreadPoolThread:真
GetStringAsync.ContinueWith,s_mark:0
IsThreadPoolThread:真
进入退出...
我认为这可能是足够的证据证实该理论认为一个IO绑定延续的确实的发生在一个IOCP线程。
有一个良好的阅读,相关:没有线程的的斯蒂芬·克利里。
I have the following WCF contract:
[ServiceContract(Namespace = "http://abc/Services/AdminService")]
public interface IAdminService
{
[OperationContract]
string GetServiceVersion();
// More methods here
}
The GetServiceVersion
is a simple method returning some string. It is used as a ping to check whether the service is reachable.
Now I would like to call it asynchronously, thinking it would be more efficient than using .NET threads to call it in background.
So, I have come up with the following interface just for that purpose:
[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();
}
This makes it possible to invoke the GetServiceVersion
API asynchronously:
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
}
});
The code works.
My question is this - does it utilize the IOCP to invoke the continuation?
In general, is there a way to know whether a continuation is invoked through IOCP or not (in the debugger, if needed) ?
P.S.
Here is the stack trace of my async WCF method continuation:
> *** 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]
Now, this stack trace looks very similar to the one I get for a method called from Task.Factory.StartNew
, which is indeed Thread Pool based:
> *** 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]
First, you'd need to add TaskContinuationOptions.ExecuteSynchronously
, to make sure the continuation callback is called on the same thread the async IO operation has been finalized:
return miniAdminService.GetServiceVersionAsync().ContinueWith(t =>
{
if (t.Exception != null)
{
// The Admin Service seems to be unavailable
}
else
{
// The Admin Service is available
}
}, TaskContinuationOptions.ExecuteSynchronously);
Apparently, there is no API in .NET to tell if the thread is a IOCP pool thread. You can only tell if the thread is a thread pool thread (Thread.CurrentThread.IsThreadPoolThread
), which is true
for IOCP threads too.
In Win32, an IOCP thread pool is created with CreateIoCompletionPort
API, but I couldn't find a Win32 API to check if the thread belongs to such pool, either.
So, here is a bit contrived example to check this theory in practice, using HtppClient
as the test vehicle. First, we make sure all non-IOCP threads have populated the ThreadStatic
variable s_mark
with -1
. Then we initiate an IO-bound operation and check s_mark
on thread where the IO-bound operation gets completed:
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();
}
}
}
The output:
Task.Run, s_mark: -1 IsThreadPoolThread: True GetStringAsync.ContinueWith, s_mark: 0 IsThreadPoolThread: True Enter to exit...
I think this might be enough evidence to confirm the theory that an IO-bound continuation does happen on an IOCP thread.
A good read, related: "There Is No Thread" by Stephen Cleary.
这篇关于并调用异步任务基于WCF方法,利用I / O完成端口或一个线程池线程调用的延续?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!