This is how I implement this.
First, I have a base ThreadWorker class that my actual processing tasks can descend from. Note that this is Windows specific, but you can substitute boost mutex for the CriticalSection, and the POSIX implementation of semaphores is here: http://linux.die.net/man/7/sem_overview
class ThreadWorker
{
public:
ThreadWorker(void)
{
signalfinished = NULL;
forcesignal = false;
}
virtual ~ThreadWorker(void)
{
if (signalfinished!=NULL) ReleaseSemaphore(signalfinished,1,NULL);
}
DWORD ThreadId;
HANDLE threadhandle;
HANDLE signalfinished;
bool forcesignal;
static DWORD WINAPI workerthread(void *param)
{
ThreadWorker *worker = (ThreadWorker*)param;
worker->signalfinished = CreateSemaphore(NULL,0,1,NULL);
worker->RunTask();
ReleaseSemaphore(worker->signalfinished,1,NULL);
}
void StartWorker()
{
CreateThread(NULL,NULL,ThreadWorker::workerthread,this,0,&ThreadId);
}
void WaitUntilWorkerFinished()
{
DWORD waitresult;
do
{
waitresult = WaitForSingleObject(signalfinished,1000);
} while (waitresult!=WAIT_OBJECT_0 && !forcesignal);
}
virtual void RunTask()=0;
};
Then, I have a ThreadManager that manages a queue of ThreadWorker objects. Again, you can substitute mutex for CriticalSection. I prefer std::list to std::vector because vector is contiguous.
class ThreadManager
{
CRITICAL_SECTION critsec;
std::list<ThreadWorker*> taskqueue;
public:
ThreadManager(void)
{
InitializeCriticalSection(&critsec);
}
void AddTaskToQueue(ThreadWorker *task)
{
EnterCriticalSection(&critsec);
taskqueue.push_back(task);
LeaveCriticalSection(&critsec);
}
void ProcessTaskQueue()
{
while (true)
{
EnterCriticalSection(&critsec);
ThreadWorker *thistask = taskqueue.front();
taskqueue.pop_front();
LeaveCriticalSection(&critsec);
thistask->StartWorker();
}
}
~ThreadManager(void)
{
DeleteCriticalSection(&critsec);
}
};
To add a task to a queue, we need a subclass that implements ThreadWorker.
class SomeWorkerTask : public ThreadWorker
{
public:
SomeWorkerTask(void);
virtual ~SomeWorkerTask(void);
void RunTask()
{
std::cout << "Hello, I am a worker task runing on thread id " << ThreadId << std::endl;
}
};
Create a new instance of SomeWorkerTask and add it to the queue, then process the queue. In your case you will have different threads adding tasks to the queue and processing the queue, but I assume you will get the idea.
SomeWorkerTask *atask = new SomeWorkerTask();
ThreadManager manager;
manager.AddTaskToQueue(atask);
manager.ProcessTaskQueue();
If you want to know when the task has completed processing, you can call ThreadWorker::WaitUntilWorkerFinished from another thread or add the call to ProcessTaskQueue. You can modify the ThreadManager so you have one queue of waiting tasks, one queue of running tasks and a third queue of finished tasks. After you pop the task off the waiting queue, you add it to the running queue, and use the task's semaphore to determine when it has completed, then add it to the finished tasks/remove it from running tasks. Note that standard containers such as vector,map and list are not thread safe so you should always surround operations that insert/remove from the container with a mutual exclusion lock such as a critical section or mutex.
Hope that helps.