4 – Thread Pool Step 2: SettingUp the Threads
KMTask
Lets go back to the heart of thethread pool (or to those in denial, the liver): The tasks. To keep everythingmanaged and such, we’ve encapsulated the KMTaskFunc
and IKMTaskData*
inside the KMTask
class, just so we can keep things managed(and modular; so you can add new functionality like dependencies or referencesor contexts or whatever you’d like in the future).
class KMTask
{
private:
KMTaskFunc m_task;
IKMTaskData* m_data;
public:
KMTask(KMTaskFunc task, IKMTaskData* data)
{
m_task = task;
m_data = data;
}
~KMTask()
{
if(m_data!=NULL)
delete m_data;
m_data = NULL;
m_task = NULL;
}
KMTaskFunc GetTask() { return m_task; }
IKMTaskData* GetData() { return m_data; }
protected:
};
Not so bad. Just manages the datafor us. This way we don’t really have to explicitly delete the IKMTaskData*
inside our task.
KMThread
Here is the logic behind ourthreads: 1. Create the thread when the threadpool starts up, 2. Idle until wefind a task in the thread pool, 3. Run the task, 4. When we finish, clean upthe memory, 5. Goto 2 (6. Beat up the guy who wrote this tutorial for using agoto).
Here is a quick rundown of themembers in the threads:
KMThreadpool* m_pthreadpool;
HANDLE m_hthread;
KMLock m_lock;
KMTask* m_ptask;
bool m_brunning;
bool m_bpaused;
unsigned int m_uithreadID;
DWORD m_dwexit;
m_pthreadpool
is apointer to the actual thread pool class, and you’ll see why we need that injust a minute.
m_hthread
is a handle (a kind ofsmart pointer) to the thread in the Windows API.
m_lock
is the oneone lock we need to protect executing our task with.
m_ptask
is thetask we get from the thread pool.
m_brunning
is a flagthat’s set to true once the thread initializes successfully, and false if itfails or gets shut down.
m_bpaused
is… uhh…legacy code! (meaning: code I forgot to delete, but isn’t breaking anything)
m_uithreadID
is anID assigned from the Windows API.
m_dwexit
is theexit code returned once the thread shuts down for any reason.
Now there are a few accessorfunctions and such, but we want to focus on 4 main functions:
public:
void Begin();
void End();
DWORD ThreadProc();
protected:
static unsigned __stdcall cThreadProc(LPVOID _pThis)
{
return ((KMThread*)_pThis)->ThreadProc();
}
Now, you see cThreadProc()
. This function is called by the WindowsAPI when you create the thread. The API will be calling this function, and thisfunction calls ThreadProc()
, and ThreadProc()
runs our task. Indirection out thewazzoo, baby!
Next we have Begin()
. Nothing too special here for anyone whohas ever created threads before. We grab the instance of our thread pool(because it’s a singleton), then we create our thread using _beginthreadex()
, and if m_hthread
isn’t NULL
, then it was a success! Once again, verysimple.
void KMThread::Begin()
{
// Set our thread pool
m_pthreadpool = KMThreadpool::getInstance();
#if defined( _WIN32 )
// Start the thread.
m_hthread = (HANDLE)_beginthreadex( NULL,
0,
&cThreadProc,
(void*)this,
0,
&m_uithreadID );
m_brunning = true;
if( m_hthread == NULL )
{
// You can add extra error-handling here.
m_brunning = false;
}
#endif /* defined( _WIN32 ) */
}
And for every Begin()
, there must be an End()
.
void KMThread::End()
{
#if defined( _WIN32 )
if( m_hthread != NULL )
{
m_brunning = false;
WaitForSingleObject( m_hthread, INFINITE );
DWORD ExitCode;
GetExitCodeThread( m_hthread, &ExitCode );
m_dwexit = ExitCode;
CloseHandle( m_hthread );
m_hthread = NULL;
}
#endif /* defined( _WIN32 ) */
}
People familiar with somemultithreading experience may throw up a red flag and say “Hey! Why aren’t youusing _endthreadex()
? Duh!” Well, Mr. NonBeliever, _endthreadex()
in the Win32 API does not closethe handle. This explicit way of closing the thread allows me to free up allnecessary memory and allows me to get the exit codes and such. And for newbiesto threading, that WaitForSingleObject()
function is also part of the Win32 API.It waits for the handle to the thread to finish its current cycle (for an INFINITE
amount of time), and stops it fromcontinuing so we can manipulate the handle (in this case, closing the handle).
The last major function in thisclass is the full-on assembily of the circulatory system of the thread pool:The ThreadProc()
function. This function is in charge of processing the actual task andgrabbing a new task once we finish. However, it’s not as simple as just run thetask and get a new one from the queue. We have to be safe and slick when we dothis. Here’s a look at the code:
DWORD KMThread::ThreadProc()
{
m_ptask = NULL;
// The main thread-loop. As long as this loop
// is running, the thread stays alive.
while(m_brunning)
{
Sleep(1);
// The thread pauses when it finishes a task.
// Adding a task resumes it.
if(m_ptask != NULL)
{
m_lock.Lock();
{
KMTaskFunc task = m_ptask->GetTask();
IKMTaskData* data = m_ptask->GetData();
// Run the actual task
if(task != NULL && data != NULL)
{
task(data);
}
// Task is complete.
delete m_ptask;
m_ptask = NULL;
}
m_lock.Unlock();
}
// If we're finished with our task, grab a new one.
if(m_ptask == NULL && m_pthreadpool->IsProcessing() == true)
{
m_ptask = m_pthreadpool->m_qtaskList.pop();
}
}
return 0;
}
The thread is almost set up asits own program, with the main thread loop in there. It’s set up so as long asthe thread is running (set by either begin()
, end()
or by the thread pool), it will do thework inside.
First thing we do inside this loop is Sleep(1)
. When I originally wrote the thread pool,I wasn’t calling Sleep()
, and all my threads became processor hogs. If you downloaded the code orare writing it yourself, go ahead and comment out the Sleep()
call and watch your computer burn!
Next, we check to see if the m_ptask
is NULL
.
- If it isn’t, then we go ahead and process it. We lock around the tasks to protect the data from getting possibly modified by another thread, and after we call the task (like you would with any normal function) we go and clean up the data.
- If it is, check the thread pool and get the next task in the queue.
And that’s our threads! Nifty!Now on to the main brain of the outfit… The interface for all thismultithreaded goodness…
转自:http://keithmaggio.wordpress.com/code/c-win32-thread-pool-manager/4-step2/