Click here to Skip to main content
16,016,669 members
Articles / Programming Languages / C#
Article

Changing the default limit of 25 threads of ThreadPool class

Rate me:
Please Sign up or sign in to vote.
4.62/5 (11 votes)
14 Jun 20038 min read 100.1K   433   20   7
Changing the 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 thread limit of 25 threads of ThreadPool class per processor. But believe me its 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 the architecture of the application. The Threadpool is useful for managing threads that are usually in a wait state and that take only a short amount of time to do their work. If still you would like to change the default limit of 25 threads then here you go.

Including the header file

The first problem which I faced as being a VC++ developer is how to include mscoree.h header file in C# project. Basically, we can not include header files in C# project because C# is purely object oriented programming language. The header files are full of constants, which wouldn’t fit well anyway in this.

What is the mscorree.h file?

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

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

What is interop?

The .NET managed applications can leverage existing COM components. The COM components inter-operate with the .NET runtime through an interop layer that will handle all the plumbing between translating messages that pass back and forth between managed runtime and the COM component operating in the unmanaged realm, and vice versa. Well, as you probably know, the programming model of COM and .NET differs greatly. The differences are too great to cover in detail right here. Hence, it's pretty obvious that some form of "Difference Manager" is needed. That’s where COM interop comes into picture. Because of the COM interop, COM objects become usable from .NET object. COM Interop provides access to existing COM components without requiring that the original component be modified.

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

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

  • 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.

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

  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

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

Creating a COM Class Wrapper

In our C# code, to reference COM objects and interfaces defined in Mscoree.h file, we need to include a .NET Framework definition for the COM interfaces in our C# build. According to Microsoft documentation, "The easiest way to do this is to use TlbImp.exe (Type Library Importer), a command-line tool included in the .NET Framework SDK. TlbImp converts a COM type library into .NET Framework metadata — effectively creating a managed wrapper that can be called from any managed language. .NET Framework metadata created with TlbImp can be included in a C# build via the /R compiler option."

Unfortunately, TlbImp.exe utility cannot handle the definitions in the mscoree typelib. Hence, (I guess) the only one alternative is available i.e. to manually define the COM definitions in C# source code using C# attributes. Once we create the C# source mapping, then we can simply compile the C# source code to produce the managed wrapper for the COM objects defined in the mscoree.h file.

Declaring a COM coclass

The COM coclass is a COM object. The coclass definition in type library enables 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:

  • 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.

Add the following declaration of coClass to ICordThread.cs file

C#
// Declare ThreadManager as a COM coclass:
[
// CLSID_CorRuntimeHost from MSCOREE.DLL
Guid("CB2F6723-AB3A-11D2-9C40-00C04FA30A3E"),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).
}

The ComImport attribute marks the class as an externally implemented Com class. Such a class declaration enables the use of a C# name to refer to 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.

Declaring a COM Interface

COM interfaces are represented in C# as interfaces with ComImport and Guid attributes. They cannot include any interfaces in their base interface list, and they must declare the interface member functions in the order that the methods appear in the COM interface.

COM interfaces declared in C# must include declarations for all members of their base interfaces with the exception of members of IUnknown and IDispatch — the .NET Framework automatically adds these.

By default, the .NET Framework provides an automatic mapping between the two styles of exception handling for COM interface methods called by the .NET Framework.

  • The return value changes to the signature of the parameter marked retval (void if the method has no parameter marked as retval).
  • The parameter marked as retval is left off of the argument list of the method.

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

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

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

Add the following code to ICordThread.cs file

C#
// derives from IUnknown interface:
[
// IID_IcorThreadPool
Guid("84680D3A-B2C1-46e8-ACC2-DBC0A359159A"), 
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]

public interface ICorThreadpool // Cannot list any base interfaces here 
{ 
// Note that IUnknown Interface members are NOT listed here:
void RegisterWaitForSingleObject(); // Don’t call. In correct signature
void UnregisterWait(); // Don’t call. In correct signature
void QueueUserWorkItem(); // Don’t call. In correct signature
void CreateTimer(); // Don’t call. In correct signature
void ChangeTimer(); // Don’t call. In correct signature
void DeleteTimer(); // Don’t call. In correct signature
void BindIoCompletionCallback(); // Don’t call. In correct signature
void CallOrQueueUserWorkItem(); // Dont call. In correct signature
void SetMaxThreads( uint MaxWorkerThreads, uint MaxIOCompletionThreads );
void GetMaxThreads( out uint MaxWorkerThreads, 
    out uint MaxIOCompletionThreads );
void GetAvailableThreads( out uint AvailableWorkerThreads, 
    out uint AvailableIOCompletionThreads );
}

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

Creating a COM Object

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:

C#
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();
:
:
    }

Using Casts Instead of QueryInterface

A C# coclass is not very useful until you can access an interface that it implements. In C++ you would navigate an object's interfaces using the QueryInterface method on the IUnknown interface. In C# you can do the same thing by explicitly casting the COM object to the desired COM interface. If the cast fails, then an invalid cast exception is thrown.

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

C#
MSCoreeTypeLib.ThreadManager threadManager =
new MSCoreeTypeLib.ThreadManager();
// QueryInterface for the ICorThreadPool interface:
MSCoreeTypeLib.ICorThreadpool ct = 
    (MSCoreeTypeLib.ICorThreadpool)threadManager;

Get/Set Max Thread Count

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

Get Max Thread Count

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

uint maxWorkerThreads;
uint maxIOThreads;
ct.GetMaxThreads(out maxWorkerThreads, out maxIOThreads);

Set Max Thread Count

maxWorkerThreads = 35;
maxIOThreads = 35;
ct.SetMaxThreads(maxWorkerThreads, maxIOThreads);

Putting it all together

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 advantage of an interface called ICorThreadpool that is implemented by the runtime. Because this interface is mentioned in mscoree.h, but not documented in mscoree.tlb, an explicit interop shim is used. See ICorThreadPool.cs in this project for details.

At first this application gets and displays the max thread count using GetMaxThreads method of ICorThreadPool interface. Then it sets the max thread count using SetMaxThreads method of IcorThreadPool interface.

It starts the 10 threads using .NET Thread pool object. At the end of the code, it again gets the max thread count using GetMaxThreads method. This time it should display the same value which being set by SetMaxThreads method.

The main purpose of this article is not to test ThreadPool class or its functionality. The only purpose of this application is to show how to adjust the max thread count of the ThreadPool class.

References

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Yash Malge began his professional career as a software engineer around 7 years ago and received various opportunities to work on hi-tech projects using the latest tools and technology, especially in the telecommunications industry. Eventually, he became a software consultant and has been providing services for companies like Motorola, Lucent, Broadwing(Cincinnati Bell), Lexmark, IBM and Atos Origin.

Comments and Discussions

 
NewsThis function was added in .NET 2.0 Pin
Quynh Nguyen23-Oct-07 18:22
Quynh Nguyen23-Oct-07 18:22 
GeneralWhy bother with this... Pin
Ollie11-Sep-07 22:55
Ollie11-Sep-07 22:55 
Generalthank you Pin
beifangying28-Mar-07 2:11
beifangying28-Mar-07 2:11 
General25 is already a high number Pin
mattiash4-Aug-03 12:28
professionalmattiash4-Aug-03 12:28 
> ...if you need more than 25 threads, I would seriously
> question the architecture of the application

You make a very important point here. If an application needs to create more threads, there is likely a better design for that application. Having more than 25 threads will create a significant overhead for the OS to deal with and adding threads will not make your applications run better.

There are plenty of articles on MSDN that describe why many threads gives overhead in context switching and kills performance and possibly scalability over CPUs.

Something you should never do is to have a server create one thread per connected client. Windows NT supports I/O completion ports as a far better solution. That way each thread can serve multiple clients, with maybe one worker thread per CPU.

The .Net framework has support for I/O completion ports in it's asynchronous methods (such as Socket.BeginXXX and NetworkStream.BeginXXX) and that will give really good performance.


Codeproject - Writing scalable server applications using IOCP
Codeproject - Developing a Truly Scalable Winsock Server using IO Completion Ports
MSDN - Windows Sockets 2.0: Write Scalable Winsock Apps Using Completion Ports
MSDN - I/O Completion Ports
GeneralBig help Pin
jfos17-Jun-03 15:49
jfos17-Jun-03 15:49 
Generalthreads load Pin
Member 199017-Jun-03 5:08
Member 199017-Jun-03 5:08 
GeneralUseful tip Pin
Anthony_Yio15-Jun-03 15:49
Anthony_Yio15-Jun-03 15:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.