简体   繁体   English

调用基于异步任务的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?

I have the following WCF contract: 我有以下WCF合同:

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

    // More methods here
}

The GetServiceVersion is a simple method returning some string. GetServiceVersion是一个返回一些字符串的简单方法。 It is used as a ping to check whether the service is reachable. 它用作ping来检查服务是否可访问。

Now I would like to call it asynchronously, thinking it would be more efficient than using .NET threads to call it in background. 现在我想异步调用它,认为它比使用.NET线程在后台调用它更有效。

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: 这使得可以异步调用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
    }
});

The code works. 代码有效。

My question is this - does it utilize the IOCP to invoke the continuation? 我的问题是 - 它是否利用IOCP来调用延续?

In general, is there a way to know whether a continuation is invoked through IOCP or not (in the debugger, if needed) ? 通常,有没有办法知道是否通过IOCP调用延续(在调试器中,如果需要)?

PS PS

Here is the stack trace of my async WCF method continuation: 这是我的异步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]  

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: 现在,这个堆栈跟踪看起来非常类似于我从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]  

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: 首先,您需要添加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);

Apparently, there is no API in .NET to tell if the thread is a IOCP pool thread. 显然,.NET中没有API来判断该线程是否是IOCP池线程。 You can only tell if the thread is a thread pool thread ( Thread.CurrentThread.IsThreadPoolThread ), which is true for IOCP threads too. 你只能告诉我们,如果线程是一个线程池线程( Thread.CurrentThread.IsThreadPoolThread ),这是true的IOCP线程太多。

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. 在Win32中,使用CreateIoCompletionPort API创建了一个IOCP线程池,但我找不到Win32 API来检查线程是否属于这样的池。

So, here is a bit contrived example to check this theory in practice, using HtppClient as the test vehicle. 因此,这是一个有点人为的例子,在实践中检验这个理论,使用HtppClient作为测试工具。 First, we make sure all non-IOCP threads have populated the ThreadStatic variable s_mark with -1 . 首先,我们确保所有非IOCP线程都使用-1填充了ThreadStatic变量s_mark Then we initiate an IO-bound operation and check s_mark on thread where the IO-bound operation gets completed: 然后我们启动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();
        }
    }
}

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. 我认为这可能足以证实在IOCP线程发生IO绑定延续的理论。

A good read, related: "There Is No Thread" by Stephen Cleary. 一个很好的阅读,相关:Stephen Cleary的“没有线索”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 未在线程池线程上安排任务继续 - Task continuation was not scheduled on thread-pool thread Task.ContinueWith捕获调用线程上下文以继续吗? - Does Task.ContinueWith capture the calling thread context for continuation? AsyncWaitHandle.WaitOne阻止CLR线程吗? 或者它是否创建了I / O完成端口? - Does AsyncWaitHandle.WaitOne block the CLR thread? Or does it create an I/O completion port? 任务继续计划在非线程池线程中进行。 为什么? - Task continuation was scheduled to non thread-pool thread. Why? 在主线程中执行Task延续的方法 - Method that executes a continuation of a Task in the main thread 异步调用在当前进程中是否需要一个额外的线程,或者在线程池中使用另一个线程? - Does an asynchronous call require an extra thread in the current process or use another thread in a thread pool? 如果该线程来自线程池,Task.Wait会释放该线程吗? - Does Task.Wait release the calling thread if that thread is from the thread pool? UI 线程上的任务延续 - Task continuation on UI thread ConfigureAwait 将延续推送到池线程 - ConfigureAwait pushes the continuation to a pool thread Task.Delay 真的像 I/O 操作那样异步吗,即它是否依赖硬件和中断而不是线程? - Is Task.Delay truly asynchronous like an I/O operation is, i.e. does it rely on hardware and interrupts instead of a thread?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM