<html>
<head>
<base href="https://wiki.asterisk.org/wiki">
<link rel="stylesheet" href="/wiki/s/2036/1/7/_/styles/combined.css?spaceKey=TOP&forWysiwyg=true" type="text/css">
</head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
<h2><a href="https://wiki.asterisk.org/wiki/display/TOP/Thread+Pools">Thread Pools</a></h2>
<h4>Page <b>edited</b> by <a href="https://wiki.asterisk.org/wiki/display/~mmichelson">Mark Michelson</a>
</h4>
<br/>
<h4>Changes (45)</h4>
<div id="page-diffs">
<table class="diff" cellpadding="0" cellspacing="0">
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" >{warning} <br> <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">In [Component Threading Design for Asynchronous Operations], the {{Workqueue}} interface was defined, and an implementation, called a {{SimpleWorkQueue}} was proposed. While the {{SimpleWorkQueue}} is a good implementation for instances where a single thread is needed, another implementation is needed when multiple threads are required. <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;">Based on content from [Component Threading Design for Asynchronous Operations], the {{Workqueue}} interface and a subclass, {{SimpleWorkQueue}}, were created. While the {{SimpleWorkQueue}} is a good method for asynchronously queuing tasks, it only uses a single thread of execution. This page works to define a method for asynchronously assigning tasks to multiple threads. This pattern is referred to as a thread pool throughout the document. <br></td></tr>
<tr><td class="diff-unchanged" > <br>h1. Configuration considerations <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" >h5. Max Size <br> <br></td></tr>
<tr><td class="diff-changed-lines" >One valid concern is letting the thread pool grow to an unreasonable size, so an obvious configuration option for dynamic thread pools is a maximum size. Once the thread pool has reached its <span class="diff-changed-words">max<span class="diff-added-chars"style="background-color: #dfd;">imum</span></span> capacity it will no longer grow, even if policy states that the load is such that it should grow more. <br></td></tr>
<tr><td class="diff-unchanged" > <br>h5. Growth Conditions <br></td></tr>
<tr><td class="diff-changed-lines" >What also needs to be configurable is the determination of when to increase the size of the thread pool. System load <span class="diff-added-words"style="background-color: #dfd;">is the typical metric used, and it</span> can be determined in several ways, including <br></td></tr>
<tr><td class="diff-unchanged" > <br>* Frequency of tasks being queued <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;">* Ratio of frequency of tasks being queued to frequency of tasks being completed <br></td></tr>
<tr><td class="diff-changed-lines" >* Number of tasks <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">in threads</span> <span class="diff-added-words"style="background-color: #dfd;">currently queued</span> <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">* Average time delta between a task's being queued and its execution (Turnaround time) <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;">* Turnaround time of task execution (i.e. timestamp when task completes minus timestamp when task is queued) <br></td></tr>
<tr><td class="diff-unchanged" > <br>h5. Growth amount <br></td></tr>
<tr><td class="diff-changed-lines" >Once it's been decided that the thread pool should grow, by how many threads should the pool grow? <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">Potential</span> <span class="diff-added-words"style="background-color: #dfd;">Common</span> methods <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">are to grow</span> <span class="diff-added-words"style="background-color: #dfd;">include growing</span> by a fixed amount or <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">to double</span> <span class="diff-added-words"style="background-color: #dfd;">doubling</span> the size of the thread pool. <br></td></tr>
<tr><td class="diff-unchanged" > <br>h3. Shrinking Policy <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" >h5. Min Size <br> <br></td></tr>
<tr><td class="diff-changed-lines" >It's useful to be able to shrink the thread pool, but it <span class="diff-added-words"style="background-color: #dfd;">is</span> also <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">seems prudent</span> <span class="diff-added-words"style="background-color: #dfd;">wise</span> to make sure the thread pool doesn't get *too* small. Specifying a minimum size will allow for the thread pool to shrink only to a specified minimum. <br></td></tr>
<tr><td class="diff-unchanged" > <br>h5. Shrink Conditions <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" >h3. Configuration conclusions <br> <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">Thread pools have a lot of potential knobs to turn when it comes to defining their behavior. Each thread pool usage will have its own unique needs, so it seems like the best way to control thread pool behavior is not through configuration of the thread pool itself, but rather by extending the thread pool to be able to call into user-provided code in order to determine how to behave. You can think of this like a microcosm of the extension point/hook model used by Asterisk SCF components. <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;">Thread pools have a lot of potential knobs to turn when it comes to defining their behavior. Each thread pool usage will have its own unique needs, so it seems like the best way to control thread pool behavior is not through configuration of the thread pool itself, but rather by having a listener make decisions based on current activity of the thread. <br></td></tr>
<tr><td class="diff-unchanged" > <br>h1. The implementation <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" >{code: language=c++} <br>/** <br></td></tr>
<tr><td class="diff-changed-lines" >* A base class for <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">defining growth</span> <span class="diff-added-words"style="background-color: #dfd;">receiving notifications</span> <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> * policies for a thread pool. <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> * from a thread pool <br></td></tr>
<tr><td class="diff-unchanged" > */ <br></td></tr>
<tr><td class="diff-changed-lines" >class <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">GrowthPolicy</span> <span class="diff-added-words"style="background-color: #dfd;">ThreadPoolListener</span> <br></td></tr>
<tr><td class="diff-unchanged" >{ <br>public: <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> // Determine if the growth policy declares that <br> // it is time for the thread pool to grow. <br> // The thread pool is provided in case it is <br> // needed in order to determine whether <br> // growth is necessary. <br> virtual bool shouldGrow(const ThreadPool &pool) = 0; <br></td></tr>
<tr><td class="diff-unchanged" > <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> // When the thread pool is allowed to grow, this method <br> // will be called to determine by how much the thread <br> // pool should grow. The thread pool is provided to <br> // this function in case the GrowthPolicy needs to <br> // gather statistics from it to determine its answer. <br> virtual size_t growthInterval(const ThreadPool &pool) = 0; <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> // The thread pool will call this when its construction has completed <br> // This allows for the listener to keep a reference to the pool for future <br> // needs. <br> virtual void poolCreated(ThreadPool &pool); <br></td></tr>
<tr><td class="diff-unchanged" > <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> // The thread pool will call this to determine which threads <br> // may be removed from the pool. If no threads may be <br> // removed, then an empty vector is returned. <br> virtual std::vector<boost::shared_ptr<PoolId> > killableThreads(const ThreadPool &pool) = 0; <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> // Called by the ThreadPool whenever a task is enqueued. <br> virtual void taskEnqueued(const boost::shared_ptr<PoolId> &poolId, unsigned long taskId) = 0; <br> <br> // Called by the ThreadPool whenever a task has finished executing. <br> // XXX This is not possible with the current implementation of WorkQueue <br> virtual void taskCompleted(const boost::shared_ptr<PoolId> &id, unsigned long taskId) = 0; <br> <br></td></tr>
<tr><td class="diff-unchanged" >protected: <br> // You can't construct one of these. You must use a subclass. <br></td></tr>
<tr><td class="diff-changed-lines" ><span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">GrowthPolicy();</span> <span class="diff-added-words"style="background-color: #dfd;">ThreadPoolListener();</span> <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> ThreadPool &mPool; <br></td></tr>
<tr><td class="diff-unchanged" >}; <br> <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" >{ <br>public: <br></td></tr>
<tr><td class="diff-changed-lines" >//Create thread pool with given <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">growth policy</span> <span class="diff-added-words"style="background-color: #dfd;">listener</span> and initial number of threads. <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> //If growthPolicy is 0, then the thread pool will be static, i.e. it will not grow or shrink <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> //It is perfectly valid for the listener to be 0 here. This indicates either a desire <br> //to control the ThreadPool from a source other than a listener or that the ThreadPool <br> //should be static. <br></td></tr>
<tr><td class="diff-changed-lines" >ThreadPool(const <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">boost::shared_ptr<GrowthPolicy> growthPolicy,</span> <span class="diff-added-words"style="background-color: #dfd;">boost::shared_ptr<ThreadPoolListener> listener,</span> size_t initThreads); <br></td></tr>
<tr><td class="diff-unchanged" > <br> //Overriding the WorkQueue methods <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" > virtual boost::shared_ptr<PoolId> enqueue(WorkPtr &w); <br> <br></td></tr>
<tr><td class="diff-changed-lines" ><span class="diff-changed-words">//Get<span class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">s</span></span> the number of threads currently in the thread pool <br></td></tr>
<tr><td class="diff-unchanged" > size_t numThreads(); <br> <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> //XXX More public methods may be necessary for the GrowthPolicy to <br> //effectively gauge whether growth is necessary. <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> //Get the number of queued tasks in the thread pool <br> //XXX The current implementation of SimpleWorkQueue prevents us <br> //from determining the exact number of tasks in a queue. <br> size_t numTasks(); <br></td></tr>
<tr><td class="diff-unchanged" > <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">private: <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> //Get the number of queued tasks in a specific queue <br> //XXX The current implementation of SimpleWorkQueue prevents us <br> //from determining the exact number of tasks in a queue. <br> size_t numTasks(const boost::shared_ptr<PoolId> &id); <br></td></tr>
<tr><td class="diff-unchanged" > <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> //Grow the thread pool by growthInterval threads. The thread pool can determine <br> //how many new threads to add by calling GrowthPolicy::growthInterval(*this) <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> //Grow the thread pool by interval threads. <br></td></tr>
<tr><td class="diff-changed-lines" >void grow(size_t <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">growthInterval);</span> <span class="diff-added-words"style="background-color: #dfd;">interval);</span> <br></td></tr>
<tr><td class="diff-unchanged" > <br></td></tr>
<tr><td class="diff-changed-lines" >//Shrink the thread pool by removing <span class="diff-added-words"style="background-color: #dfd;">threads with</span> the specified <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">threads.</span> <span class="diff-added-words"style="background-color: #dfd;">IDs.</span> <br></td></tr>
<tr><td class="diff-changed-lines" >void shrink(std::vector<boost::shared_ptr<PoolId> > <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">threads);</span> <span class="diff-added-words"style="background-color: #dfd;">ids);</span> <br></td></tr>
<tr><td class="diff-unchanged" > <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> boost::shared_ptr<GrowthPolicy> mPolicy; <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> //XXX More public methods may be necessary for the ThreadPoolListener to <br> //effectively gauge whether growth is necessary. Ideas welcome :) <br> <br>private: <br> boost::shared_ptr<ThreadPoolListener> mListener; <br></td></tr>
<tr><td class="diff-unchanged" > std::map<PoolId, boost::shared_ptr<SimpleWorkQueue> > mQueues; <br>}; <br>{code} <br> <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">So first, let's have a look at {{ThreadPool}}. It is a subclass of {{WorkQueue}}, meaning that it overrides the method to enqueue new tasks. At the heart of the {{ThreadPool}} is a map of {{SimpleWorkQueues}}. A map was chosen because it allows for easy and quick access to a specific work queue if desired. It also allows for a thread to be removed from the pool without causing any potential gaps in the container. The thread pool has private methods for growing and shrinking. <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;">So first, let's have a look at {{ThreadPool}}. It is a subclass of {{WorkQueue}}, meaning that it overrides the methods to enqueue new tasks. At the heart of the {{ThreadPool}} is a map of {{SimpleWorkQueues}}. A map is used because it allows for easy and quick access to a specific work queue if desired. It also allows for a thread to be removed from the pool without causing any potential gaps in the container. In addition to the {{WorkQueue}} overrides, {{ThreadPool}} exposes methods to allow growth or shrinkage if desired. <br></td></tr>
<tr><td class="diff-unchanged" > <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">What's more interesting is the {{GrowthPolicy}} class. {{GrowthPolicy}} offers an interface that the {{ThreadPool}} will call into to determine how it should behave. Implementors of {{GrowthPolicy}}s can define their own methods of determining if the thread pool should grow or shrink and by how much the pool should grow or shrink. <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;">The {{ThreadPoolListener}} is the more interesting part here. The {{ThreadPoolListener}} is notified whenever tasks are enqueued or completed. This way, the {{ThreadPoolListener}} can enact local policy and command the {{ThreadPool}} to grow or shrink if desired. It would be prudent to provide some {{ThreadPoolListener}}s that implement common grow/shrink policies so that {{ThreadPool}} users will not always be forced to define their own policies. <br></td></tr>
<tr><td class="diff-unchanged" > <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">To be continued again... <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;">h1. Prerequisites <br>The proposed implementation has several {{XXX}} comments to note potential improvements. Several of these are predicated on two changes to the {{WorkQueue}} or {{SimpleWorkQueue}} classes. The simpler change will be that {{SimpleWorkQueue}} will need to expose a public method to determine the number of tasks currently in a queue. The trickier change will be to add some sort of way for a {{WorkQueue}} to be notified when a task has finished executing. Without doing a whole lot of thinking about it, a way that comes to mind is to add a protected method to {{WorkQueue}} called {{taskCompleted}}. Then add a call to {{WorkQueue::Work}}'s destructor to call {{taskCompleted}}. <br></td></tr>
</table>
</div> <h4>Full Content</h4>
<div class="notificationGreySide">
<div class='panelMacro'><table class='warningMacro'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/wiki/images/icons/emoticons/forbidden.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td>Under construction</td></tr></table></div>
<p>Based on content from <a href="/wiki/display/TOP/Component+Threading+Design+for+Asynchronous+Operations" title="Component Threading Design for Asynchronous Operations">Component Threading Design for Asynchronous Operations</a>, the <tt>Workqueue</tt> interface and a subclass, <tt>SimpleWorkQueue</tt>, were created. While the <tt>SimpleWorkQueue</tt> is a good method for asynchronously queuing tasks, it only uses a single thread of execution. This page works to define a method for asynchronously assigning tasks to multiple threads. This pattern is referred to as a thread pool throughout the document.</p>
<h1><a name="ThreadPools-Configurationconsiderations"></a>Configuration considerations</h1>
<p>Thread pools have a potential for lots of knobs to be tweaked regarding their operation.</p>
<h3><a name="ThreadPools-StaticorDynamic"></a>Static or Dynamic</h3>
<p>A static thread pool is one in which a set number of threads are created at the time that the thread pool is constructed. The thread pool will always have this number of worker threads no matter the amount of system load. Dynamic thread pools start with some initial number of threads and then can grow or shrink depending on the amount of load on the system. Since static thread pools are simple, we will focus on the dynamic thread pool configuration.</p>
<h3><a name="ThreadPools-GrowthPolicy"></a>Growth Policy</h3>
<p>It's obvious that a dynamic thread pool should grow when needed, but there needs to be some set of rules to govern the growth. </p>
<h5><a name="ThreadPools-MaxSize"></a>Max Size</h5>
<p>One valid concern is letting the thread pool grow to an unreasonable size, so an obvious configuration option for dynamic thread pools is a maximum size. Once the thread pool has reached its maximum capacity it will no longer grow, even if policy states that the load is such that it should grow more.</p>
<h5><a name="ThreadPools-GrowthConditions"></a>Growth Conditions</h5>
<p>What also needs to be configurable is the determination of when to increase the size of the thread pool. System load is the typical metric used, and it can be determined in several ways, including</p>
<ul>
        <li>Frequency of tasks being queued</li>
        <li>Ratio of frequency of tasks being queued to frequency of tasks being completed</li>
        <li>Number of tasks currently queued</li>
        <li>Turnaround time of task execution (i.e. timestamp when task completes minus timestamp when task is queued)</li>
</ul>
<h5><a name="ThreadPools-Growthamount"></a>Growth amount</h5>
<p>Once it's been decided that the thread pool should grow, by how many threads should the pool grow? Common methods include growing by a fixed amount or doubling the size of the thread pool.</p>
<h3><a name="ThreadPools-ShrinkingPolicy"></a>Shrinking Policy</h3>
<p>A dynamic thread pool may grow as needed, but should the thread pool shrink if the system load has decreased? It certainly seems like it would be desired. Otherwise a spike in load could cause a permanent allocation of threads which would barely ever be used.</p>
<h5><a name="ThreadPools-MinSize"></a>Min Size</h5>
<p>It's useful to be able to shrink the thread pool, but it is also wise to make sure the thread pool doesn't get <b>too</b> small. Specifying a minimum size will allow for the thread pool to shrink only to a specified minimum.</p>
<h5><a name="ThreadPools-ShrinkConditions"></a>Shrink Conditions</h5>
<p>Like with growth, it needs to be determined what metric should be used to determine when a thread pool may shrink. Determining inactivity in a thread can really only be done by measuring the time that a thread has been idle. After a certain cutoff point, the thread can be considered "dead" and be removed from the pool. Unlike with thread pool growth, you can't really have a configured shrink amount since the pool must shrink a single thread at a time.</p>
<h3><a name="ThreadPools-Initialsize"></a>Initial size</h3>
<p>Both dynamic and static thread pools will need an initial size. The static thread pool will just remain that size forever whereas the dynamic thread pool will adjust based on other parameters.</p>
<h3><a name="ThreadPools-Configurationconclusions"></a>Configuration conclusions</h3>
<p>Thread pools have a lot of potential knobs to turn when it comes to defining their behavior. Each thread pool usage will have its own unique needs, so it seems like the best way to control thread pool behavior is not through configuration of the thread pool itself, but rather by having a listener make decisions based on current activity of the thread.</p>
<h1><a name="ThreadPools-Theimplementation"></a>The implementation</h1>
<p>With all the configuration considerations, here is a sample class for a thread pool. Liberties have been taken with regards to namespaces.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<script type="syntaxhighlighter" class="toolbar: false; theme: Confluence; brush: java; gutter: false"><![CDATA[
/**
* A base class for receiving notifications
* from a thread pool
*/
class ThreadPoolListener
{
public:
// The thread pool will call this when its construction has completed
// This allows for the listener to keep a reference to the pool for future
// needs.
virtual void poolCreated(ThreadPool &pool);
// Called by the ThreadPool whenever a task is enqueued.
virtual void taskEnqueued(const boost::shared_ptr<PoolId> &poolId, unsigned long taskId) = 0;
// Called by the ThreadPool whenever a task has finished executing.
// XXX This is not possible with the current implementation of WorkQueue
virtual void taskCompleted(const boost::shared_ptr<PoolId> &id, unsigned long taskId) = 0;
protected:
// You can't construct one of these. You must use a subclass.
ThreadPoolListener();
ThreadPool &mPool;
};
class ThreadPool : public WorkQueue
{
public:
//Create thread pool with given listener and initial number of threads.
//It is perfectly valid for the listener to be 0 here. This indicates either a desire
//to control the ThreadPool from a source other than a listener or that the ThreadPool
//should be static.
ThreadPool(const boost::shared_ptr<ThreadPoolListener> listener, size_t initThreads);
//Overriding the WorkQueue methods
virtual void enqueue(WorkPtr &w, PoolId id);
virtual boost::shared_ptr<PoolId> enqueue(WorkPtr &w);
//Get the number of threads currently in the thread pool
size_t numThreads();
//Get the number of queued tasks in the thread pool
//XXX The current implementation of SimpleWorkQueue prevents us
//from determining the exact number of tasks in a queue.
size_t numTasks();
//Get the number of queued tasks in a specific queue
//XXX The current implementation of SimpleWorkQueue prevents us
//from determining the exact number of tasks in a queue.
size_t numTasks(const boost::shared_ptr<PoolId> &id);
//Grow the thread pool by interval threads.
void grow(size_t interval);
//Shrink the thread pool by removing threads with the specified IDs.
void shrink(std::vector<boost::shared_ptr<PoolId> > ids);
//XXX More public methods may be necessary for the ThreadPoolListener to
//effectively gauge whether growth is necessary. Ideas welcome :)
private:
boost::shared_ptr<ThreadPoolListener> mListener;
std::map<PoolId, boost::shared_ptr<SimpleWorkQueue> > mQueues;
};
]]></script>
</div></div>
<p>So first, let's have a look at <tt>ThreadPool</tt>. It is a subclass of <tt>WorkQueue</tt>, meaning that it overrides the methods to enqueue new tasks. At the heart of the <tt>ThreadPool</tt> is a map of <tt>SimpleWorkQueues</tt>. A map is used because it allows for easy and quick access to a specific work queue if desired. It also allows for a thread to be removed from the pool without causing any potential gaps in the container. In addition to the <tt>WorkQueue</tt> overrides, <tt>ThreadPool</tt> exposes methods to allow growth or shrinkage if desired.</p>
<p>The <tt>ThreadPoolListener</tt> is the more interesting part here. The <tt>ThreadPoolListener</tt> is notified whenever tasks are enqueued or completed. This way, the <tt>ThreadPoolListener</tt> can enact local policy and command the <tt>ThreadPool</tt> to grow or shrink if desired. It would be prudent to provide some <tt>ThreadPoolListener}}s that implement common grow/shrink policies so that {{ThreadPool</tt> users will not always be forced to define their own policies. </p>
<h1><a name="ThreadPools-Prerequisites"></a>Prerequisites</h1>
<p>The proposed implementation has several <tt>XXX</tt> comments to note potential improvements. Several of these are predicated on two changes to the <tt>WorkQueue</tt> or <tt>SimpleWorkQueue</tt> classes. The simpler change will be that <tt>SimpleWorkQueue</tt> will need to expose a public method to determine the number of tasks currently in a queue. The trickier change will be to add some sort of way for a <tt>WorkQueue</tt> to be notified when a task has finished executing. Without doing a whole lot of thinking about it, a way that comes to mind is to add a protected method to <tt>WorkQueue</tt> called <tt>taskCompleted</tt>. Then add a call to <tt>WorkQueue::Work</tt>'s destructor to call <tt>taskCompleted</tt>.</p>
</div>
<div id="commentsSection" class="wiki-content pageSection">
<div style="float: right;">
<a href="https://wiki.asterisk.org/wiki/users/viewnotifications.action" class="grey">Change Notification Preferences</a>
</div>
<a href="https://wiki.asterisk.org/wiki/display/TOP/Thread+Pools">View Online</a>
|
<a href="https://wiki.asterisk.org/wiki/pages/diffpagesbyversion.action?pageId=11338403&revisedVersion=4&originalVersion=3">View Changes</a>
|
<a href="https://wiki.asterisk.org/wiki/display/TOP/Thread+Pools?showComments=true&showCommentArea=true#addcomment">Add Comment</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>