Changing the C# Default Limit of 25 Threads of ThreadPool Class

Introduction

According to the Microsoft documentation., "The thread pool is created the first time you create an instance of the ThreadPool class. The thread pool has a default limit of 25 threads per available processor, which could be changed using CorSetMaxThreads as defined in the mscoree.h file."

It appears to be a simple function call to change the default threadlimit of 25 threads of ThreadPool class per processor. But believe meits not that easy at all. I have found the way to do this. But however,if you need more than 25 threads, I would seriously question thearchitecture of the application. The Threadpool is useful for managingthreads that are usually in a wait state and that take only a shortamount of time to do their work. If still you would like to change thedefault limit of 25 threads then here you go.

  • Include header file
  • Thefirst problem which I faced as being a VC++ developer is how to include"mscoree.h" header file in C# project. Basically, we can not includeheader files in C# project because C# is purely object orientedprogramming language. The header files are full of constants, whichwouldn?t fit well anyway in this.

      What is the mscorre.h file?

      Basically, MSCoree.dll is the Microsoft .NETRuntime Execution Engine, which hosts the .NET CLR (Common LanguageRuntime) in an unmanaged environment, and exposes a generic functions.

      Then the main question remains is How to call a unmanaged code(Mscoree.dll functions) from managed code (C# application)? The onlyway to do this is to interop out to unmanaged code and calls theunmanaged interface method to increase the max thread count.

    1. What is the interop?
    2. The .Net managed applications can leverageexisting COM components. The COM components inter-operate with the .Netruntime through an interop layer that will handle all the plumbingbetween translating messages that pass back and forth between managedruntime and the COM component operating in the unmanaged realm, andvice versa. Well, as you probably know that the programming model ofCOM and .Net differs greatly. The differences are too great to cover indetail right here. Hence, Its pretty obvious that some form of"Difference Manager" is needed. That?s where COM interop comes intopicture. Because of the COM interop, COM objects become usable from.Net object. COM Interop provides access to existing COM componentswithout requiring that the original component be modified.

    3. How to use C# to interoperate with COM objects and its interfaces defined in Mscoree.h file?

    <dir><dir><dir><dir>

    C# uses .NET Framework facilities to perform COM Interop. C# has support for:

    </dir></dir></dir></dir>

      <dir><dir>
      • Creating COM objects.
      • Determining if a COM interface is implemented by an object.
      • Calling methods on COM interfaces.
      • Implementing objects and interfaces that can be called by COM clients.

      </dir></dir>

    <dir><dir><dir><dir>

    There are the following steps to create the COM interop for the mscoree.h file and putting all together.

    </dir></dir></dir></dir>

    1.  
      1.  
        1. Creating a COM Class Wrapper
        2. Declaring a COM coclass
        3. Declaring a COM Interface
        4. Creating a COM Object
        5. Using Casts Instead of QueryInterface
        6. Set Max thread count

    <dir><dir><dir><dir>

    At first, add empty C# code file called "ICordThread,cs" to C# project.

    </dir></dir></dir>

     

    </dir>

    1.  
      1.  
        1. Creating a COM Class Wrapper.
        2. In ourC# code, to reference COM objects and interfaces defined in Mscoree.hfile, we need to include a .NET Framework definition for the COMinterfaces in our C# build. According to Microsoft documentation, "Theeasiest way to do this is to use TlbImp.exe (Type Library Importer), acommand-line tool included in the .NET Framework SDK. TlbImp converts aCOM type library into .NET Framework metadata ? effectively creating amanaged wrapper that can be called from any managed language. .NETFramework metadata created with TlbImp can be included in a C# buildvia the /R compiler option."

          Unfortunately, TlbImp.exeutility cannot handle the definitions in the mscoree typelib. Hence, (Iguess) the only one alternative is available i.e. to manually definethe COM definitions in C# source code using C# attributes. Once wecreate the C# source mapping, then we can simply compile the C# sourcecode to produce the managed wrapper for the COM objects defined in themscoree.h file.

           

        3. Declaring a COM coclass

    <dir><dir><dir><dir>

    TheCOM coclass is a COM object. The coclass definition in type libraryenables to list the interfaces and attributes of a COM object.

    The COM coclasses are represented in C# as classes. These classes must have the ComImport attribute associated with them. The following restrictions apply to these classes:

    </dir></dir></dir></dir>

      <dir><dir>
      • The class must not inherit from any other class.
      • The class must implement no interfaces.
      • The class must also have a Guid attribute that sets the globally unique identifier (GUID) for the class.

      </dir></dir>

    <dir><dir><dir><dir>

    Add the following declaration of coClass to "ICordThread.cs" file

    // Declare ThreadManager as a COM coclass:

    [

    // CLSID_CorRuntimeHost from MSCOREE.DLL

    Guid("CB2F6723-AB3A-11D

    2-9C40-0
    0C04FA30A3E"),ComImport

    ]

    class ThreadManager // Cannot have a base class or

    // interface list here.

    {

    // Cannot have any members here

    // NOTE that the C# compiler will add a default constructor

    // for you (no parameters).

    }

    TheComImport attribute marks the class as an externally implemented Comclass. Such a class declaration enables the use of a C# name to referto a COM class.

    The above code declares a class ThreadManager as a class imported from COM that has a CLSID of "CB2F6723-AB3A-11D2-9C40-00C04FA30A3E". Instantiating a ThreadManager instance causes a corresponding COM instantiation.

    </dir></dir></dir></dir>

    1.  
      1.  
        1. Declaring a COM Interface

    <dir><dir><dir><dir>

    COM interfaces are represented in C# as interfaces with ComImport and Guidattributes. They cannot include any interfaces in their base interfacelist, and they must declare the interface member functions in the orderthat the methods appear in the COM interface.

    COM interfaces declared inC# must include declarations for all members of their base interfaceswith the exception of members of IUnknown and IDispatch ? the .NET Framework automatically adds these.

    By default, the .NETFramework provides an automatic mapping between the two styles ofexception handling for COM interface methods called by the .NETFramework.

    </dir></dir></dir></dir>

      <dir><dir>
      • The return value changes tothe signature of the parameter marked retval (void if the method has noparameter marked as retval).
      • The parameter marked as retval is left off of the argument list of the method.

      </dir></dir>

    <dir><dir><dir><dir>

    Any non-success return value will cause a System.COMException exception to be thrown.

    The following code shows aCOM interface declared interface declared in C# (note that the methodsuse the COM error-handling approach).

    The ICorThreadpool interfaceis documented (prototypes only) in mscoree.h, but is not made availablefrom mscoree.tlb. So the following interop stub lets us get our handson the interface in order to query/control the CLR-managed thread pool.Because we are interested in adjusting maximum thread count of thethread pool configuration, most of the members are actually invalid andcannot be called in their current form.

    Add the following code to "ICordThread.cs" file

    // derives from IUnknown interface:

    [

    // IID_IcorThreadPool

    <dir><dir>

    Guid("84680D3A-B2C1-46e8-ACC2-DBC0A359159A"),

    </dir></dir>

    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)

    ]

    public interface ICorThreadpool // Cannot list any base interfaces here

    {

    // Note that IUnknown Interface members are NOT listed here:

    <dir><dir><dir><dir>

    void RegisterWaitForSingleObject(); // Don?t call. In correct //signature

    </dir></dir></dir></dir>

    void UnregisterWait(); // Don?t call. In correct signature

    void QueueUserWorkItem(); // Don?t call. In correct signature

    <dir><dir>

    void CreateTimer(); // Don?t call. In correct signature

    void ChangeTimer(); // Don?t call. In correct signature

    </dir></dir>

    void DeleteTimer(); // Don?t call. In correct signature

    <dir><dir><dir><dir>

    void BindIoCompletionCallback(); // Don?t call. In correct // signature

    void CallOrQueueUserWorkItem(); // Dont call. In correct //signiture

    void SetMaxThreads( uint MaxWorkerThreads, uint MaxIOCompletionThreads );

    void GetMaxThreads( out uint MaxWorkerThreads, out uint MaxIOCompletionThreads );

    void GetAvailableThreads( out uint AvailableWorkerThreads, out uint AvailableIOCompletionThreads );

    </dir></dir></dir></dir>

    }

    </dir></dir></dir></dir>

     

    <dir><dir><dir><dir><dir>

    Note how the C# interfacehas mapped the error-handling cases. If the COM method returns anerror, an exception will be raised on the C# side.

    </dir></dir></dir></dir></dir>

     

    1.  
      1.  
        1. Creating a COM Object
        2. COM coclasses are represented in C# as classes with a parameter less constructor. Creating an instance of this class using the new operator is the C# equivalent of calling CoCreateInstance. Using the class defined above, it is simple to instantiate the ThreadManager class:

          public static void Main()
          {
          //
          // Create an instance of a COM coclass – calls
          //
          // CoCreateInstance(CB2F6723-AB3A-11D2-9C40-00C04FA30A3E,
          // NULL, CLSCTX_ALL,
          // IID_IUnknown, &f)
          //
          // returns null on failure.
          //

          MSCoreeTypeLib.ThreadManager threadManager =

          new MSCoreeTypeLib.ThreadManager();

          :

          :

          }

           

        3. Using Casts Instead of QueryInterface
        4. A C# coclass is not very useful until you can access aninterface that it implements. In C++ you would navigate an object'sinterfaces using the QueryInterface method on the IUnknowninterface. In C# you can do the same thing by explicitly casting theCOM object to the desired COM interface. If the cast fails, then aninvalid cast exception is thrown.

          The cast is required since interop shims like CorRuntimeHost cannothave methods, which would be required if it were to advertise that itimplements ICorThreadPool statically).

           

          MSCoreeTypeLib.ThreadManager threadManager =

          new MSCoreeTypeLib.ThreadManager();

          // QueryInterface for the ICorThreadPool interface:

          MSCoreeTypeLib.ICorThreadpool ct =

          (MSCoreeTypeLib.ICorThreadpool)threadManager;

           

        5. Get/Set Max Thread Count

    <dir><dir><dir><dir><dir>

    The GetMaxThreads and SetMaxThreads methods can be called using above ICorThreadpool interface object ct as shown bellow.

    </dir></dir></dir></dir></dir>

     

    <dir><dir><dir><dir>

    Get Max Thread Count

    uint maxW

    orkerThreads;

    uint maxIOThreads;

    ct.GetMaxThreads(out maxWorkerThreads, out maxIOThreads);

    IfICorThreadPool.GetMaxThreads returns 25 and 25, then that's a total of50 threads (as opposed to saying there are 25 threads max, of which upto 25 can be devoted to I/O )

    </dir></dir></dir></dir><dir><dir>

    1. Set Max Thread Count
    2. maxWorkerThreads = 35;

      maxIOThreads = 35;

      ct.SetMaxThreads(maxWorkerThreads, maxIOThreads);

       

    3. Putting all together

    </dir></dir><dir>

    This sample program demonstrates how to change the max thread count for the CLR-managed thread pool at runtime.

    This program uses COM interop to reach out and call MSCOREE.

    This program takes advantageof an interface called ICorThreadpool that is implemented by theruntime. Because this interface is mentioned in mscoree.h, but notdocumented in mscoree.tlb, an explicit interop shim is used. SeeICorThreadPool.cs in this project for details.

    At first this applicationgets and displays the max thread count using GetMaxThreads method ofICorThreadPool interface. Then it sets the max thread count usingSetMaxThreads method of IcorThreadPool interface.

    It starts the 10 threadsusing .Net Thread pool object. At the end of the code, it again getsthe max thread count using GetMaxThreads method. This time it shoulddisplay the same value which being set by SetMaxThreads method.

    The main purpose of thisarticle is not to test ThreadPool class or its functionality. The onlypurpose of this application is to show how to adjust the max threadcount of the ThreadPool class..

    </dir>

    Twitter Digg Delicious Stumbleupon Technorati Facebook Email

    No comments yet... Be the first to leave a reply!