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

查看:179
本文介绍了并调用异步任务基于WCF方法,利用I / O完成端口或一个线程池线程调用的延续?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下的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 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆