Using Barcode drawer for .NET Control to generate, create QR-Code image in .NET applications.

www.OnBarcode.com

The System.Threading.ThreadPool class offers several static methods that you can call to manipulate the number of threads in the thread pool: GetMaxThreads, SetMaxThreads, GetMinThreads, SetMinThreads, and GetAvailableThreads . I highly recommend that you do not call any of these methods . Playing with thread pool limits usually results in making an application perform worse, not better . If you think that your application needs hundreds or thousands of threads, there is something seriously wrong with the architecture of your application and the way that it s using threads . This chapter and 27 demonstrate the proper way to use threads .

Using Barcode creation for ASP.NET Control to generate, create GTIN - 12 image in ASP.NET applications.

www.OnBarcode.com

Figure 26-1 shows the various data structures that make up the worker threads part of the thread pool . The ThreadPool.QueueUserWorkItem method and the Timer class always queue work items to the global queue . Worker threads pull items from this queue using a first-in-first-out (FIFO) algorithm and process them . Since multiple worker threads can be removing items from the global queue simultaneously, all worker threads contend on a thread synchronization lock to ensure that two or more threads don t take the same work item . This thread synchronization lock can become a bottleneck in some applications, thereby limiting scalability and performance to some degree .

Now let s talk about Task objects scheduled using the default TaskScheduler (obtained by querying TaskScheduler s static Default property) .4 When a non-worker thread schedules a Task, the Task is added to the global queue . But, each worker thread has its own local queue and when a worker thread schedules a Task, the Task is added to calling the thread s local queue .

Other TaskScheduler-derived objects may exhibit behavior different from what I describe here .

Part V

Threading

When a worker thread is ready to process an item, it always checks its local queue for a Task first . If a Task exists, the worker thread removes the Task from its local queue and processes the item . Note that a worker thread pulls tasks from its local queue using a last-in-first-out (LIFO) algorithm . Since a worker thread is the only thread allowed to access the head of its own local queue, no thread synchronization lock is required and adding and removing Tasks from the queue is very fast . A side effect of this behavior is that Tasks are executed in the reverse order that they were queued . Important Thread pools have never guaranteed the order in which queued items are processed,

especially since multiple threads could be processing items simultaneously . However, this side effect exacerbates the problem . You must make sure that your application has no expectations about the order in which queued work items or Tasks execute .

If a worker thread sees that its local queue is empty, then the worker thread will attempt to steal a Task from another worker thread s local queue . Tasks are stolen from the tail of a local queue and require that a thread synchronization lock be taken, which hurts performance a little bit . Of course, the hope is that stealing rarely occurs, so this lock is taken rarely . If all the local queues are empty, then the worker thread will extract an item from the global queue (taking its lock) using the FIFO algorithm . If the global queue is empty, then the worker thread puts itself to sleep waiting for something to show up . If it sleeps for a long time, then it will wake itself up and destroy itself, allowing the system to reclaim the resources (kernel object, stacks, TEB) that were used by the thread . The thread pool will quickly create worker threads so that the number of worker threads is equal to the value pass to ThreadPool s SetMinThreads method . If you never call this method (and it s recommended that you never call this method), then the default value is equal to the number of CPUs that your process is allowed to use as determined by your process s affinity mask . Usually your process is allowed to use all the CPUs on the machine,5 so the thread pool will quickly create worker threads up to the number of CPUs on the machine . After this many threads have been created, the thread pool monitors the completion rate of work items and if items are taking a long time to complete (the meaning of which is not documented), it creates more worker threads . If items start completing quickly, then worker threads will be destroyed .