Does calling asynchronous Task based WCF method utilize the I/O completion port or a Thread Pool thread to call the continuation?

后端 未结 1 1695
说谎
说谎 2020-12-06 12:24

I have the following WCF contract:

[ServiceContract(Namespace = \"http://abc/Services/AdminService\")]
public interface IAdminService
{
    [OperationContrac         


        
1条回答
  •  粉色の甜心
    2020-12-06 13:24

    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.

    0 讨论(0)
提交回复
热议问题