NGWS SDK Documentation  

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

ThreadPool Concepts

Queuing work items to the Thread Pool

Key Points

Used to post workitems to the threadpool. There is only one managed thread pool per process.

There are many applications that create threads that spend a great deal of time in the sleeping state waiting for an event to occur. Other threads may enter a sleeping state only to be awakened periodically to poll for a change or update status information. Thread pooling enables you to use threads more efficiently by providing your application with a pool of worker threads that are managed by the system. One thread monitors the status of all wait operations queued to the thread pool. When a wait operation has completed, a worker thread from the thread pool executes the corresponding callback function.

You can also queue work items that are not related to a wait operation to the thread pool. To request that a work item be handled by a thread in the thread pool, call the System.Threading.ThreadPool.QueueUserWorkItem from managed code or CorQueueUserWorkItem from unmanaged code. This function takes a parameter to the delegate or function respectively, that will be called by the thread selected from the thread pool. There is no way to cancel a work item after it has been queued.

Timer-queue timers and registered wait operations also use the thread pool. Their callback functions are queued to the thread pool.

The thread pool is created the first time you call ThreadPool.QueueUserWorkItem, or when a timer or registered wait operation queues a callback function. The number of threads that can be created in the thread pool is limited only by available memory. Each thread uses the default stack size and runs at the default priority. Each thread can handle up to 63 wait operations.

Also see the article “New Windows 2000 Pooling Functions Greatly Simplify Thread Management” by Jeffrey Richter in the April 1999 issue of Microsoft Systems Journal.

For reference details see: ThreadPool System.Threading.WaitCallback

IO operations that complete asynchronously

This section describes NGWS runtime IO Completions and how shows how classlibrary writers could implement async IO operations that complete. The example of how the class libraries implemented async IO on file is only an illustration. Class library writers are free to implement async IO quite differently. The samples are in pseudo code.

IO operations on OS handles can complete asynchronously. This involves associating an OS IO handle with the thread pool and specifying a callback. When IO operations complete asynchronously, they are surfaced via a thread from the thread pool and the associated callback is called.

In order for this to occur correctly, the Win32 OVERLAPPED structure needs to be prepared so that the COR infrastructure is able to route the IO operation from the thread pool to the correct callback.

The APIs have been designed so that two statements are true:

The rationale is that COR needs to be in the callback chain, because the thread pool thread needs to switch to co-operative GC mode when surfacing from the thread pool.

Additionally we may find a need to do bookkeeping in the callback. Or we may have to switch the callback to a non-IO thread. But we don't want to incur the cost of pumping all callbacks through the runtime if it turns out we have no work to do there.

In order to perform interceptions, each overlapped struct submitted to the OS in an async operation must be "packed" by the runtime. If the runtime chooses to intercept the callback, this involves recording the class libraries' callback in the overlapped struct so the runtime can finish dispatching the callback.

Another motivation for the design, which is that the runtime assumes responsibility for pinning and unpinning the overlapped structure. This would otherwise be a constant source of difficult-to-diagnose catastrophic bugs, because the lifetime of pinning doesn't match the lifetime of the PInvoke call that initiates the IO.

By calling the static method ThreadPool.BindHandle an OS handle can be associated with the ThreadPool.