Thread pool implementation using pthreads - c++

I am trying to understand the below implementation of thread pool using the pthreads. When I comment out the the for loop in the main, the program stucks, upon putting the logs it seems that its getting stuck in the join function in threadpool destructor.
I am unable to understand why this is happening, is there any deadlock scenario happening ?
This may be naive but can someone help me understand why this is happening and how to correct this.
Thanks a lot !!!
#include <stdio.h>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include <malloc.h>
#include <stdlib.h>
// Base class for Tasks
// run() should be overloaded and expensive calculations done there
// showTask() is for debugging and can be deleted if not used
class Task {
public:
Task() {}
virtual ~Task() {}
virtual void run()=0;
virtual void showTask()=0;
};
// Wrapper around std::queue with some mutex protection
class WorkQueue {
public:
WorkQueue() {
// Initialize the mutex protecting the queue
pthread_mutex_init(&qmtx,0);
// wcond is a condition variable that's signaled
// when new work arrives
pthread_cond_init(&wcond, 0);
}
~WorkQueue() {
// Cleanup pthreads
pthread_mutex_destroy(&qmtx);
pthread_cond_destroy(&wcond);
}
// Retrieves the next task from the queue
Task *nextTask() {
// The return value
Task *nt = 0;
// Lock the queue mutex
pthread_mutex_lock(&qmtx);
// Check if there's work
if (finished && tasks.size() == 0) {
// If not return null (0)
nt = 0;
} else {
// Not finished, but there are no tasks, so wait for
// wcond to be signalled
if (tasks.size()==0) {
pthread_cond_wait(&wcond, &qmtx);
}
// get the next task
nt = tasks.front();
if(nt){
tasks.pop();
}
// For debugging
if (nt) nt->showTask();
}
// Unlock the mutex and return
pthread_mutex_unlock(&qmtx);
return nt;
}
// Add a task
void addTask(Task *nt) {
// Only add the task if the queue isn't marked finished
if (!finished) {
// Lock the queue
pthread_mutex_lock(&qmtx);
// Add the task
tasks.push(nt);
// signal there's new work
pthread_cond_signal(&wcond);
// Unlock the mutex
pthread_mutex_unlock(&qmtx);
}
}
// Mark the queue finished
void finish() {
pthread_mutex_lock(&qmtx);
finished = true;
// Signal the condition variable in case any threads are waiting
pthread_cond_signal(&wcond);
pthread_mutex_unlock(&qmtx);
}
// Check if there's work
bool hasWork() {
//printf("task queue size is %d\n",tasks.size());
return (tasks.size()>0);
}
private:
std::queue<Task*> tasks;
bool finished;
pthread_mutex_t qmtx;
pthread_cond_t wcond;
};
// Function that retrieves a task from a queue, runs it and deletes it
void *getWork(void* param) {
Task *mw = 0;
WorkQueue *wq = (WorkQueue*)param;
while (mw = wq->nextTask()) {
mw->run();
delete mw;
}
pthread_exit(NULL);
}
class ThreadPool {
public:
// Allocate a thread pool and set them to work trying to get tasks
ThreadPool(int n) : _numThreads(n) {
int rc;
printf("Creating a thread pool with %d threads\n", n);
threads = new pthread_t[n];
for (int i=0; i< n; ++i) {
rc = pthread_create(&(threads[i]), 0, getWork, &workQueue);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
}
// Wait for the threads to finish, then delete them
~ThreadPool() {
workQueue.finish();
//waitForCompletion();
for (int i=0; i<_numThreads; ++i) {
pthread_join(threads[i], 0);
}
delete [] threads;
}
// Add a task
void addTask(Task *nt) {
workQueue.addTask(nt);
}
// Tell the tasks to finish and return
void finish() {
workQueue.finish();
}
// Checks if there is work to do
bool hasWork() {
return workQueue.hasWork();
}
private:
pthread_t * threads;
int _numThreads;
WorkQueue workQueue;
};
// stdout is a shared resource, so protected it with a mutex
static pthread_mutex_t console_mutex = PTHREAD_MUTEX_INITIALIZER;
// Debugging function
void showTask(int n) {
pthread_mutex_lock(&console_mutex);
pthread_mutex_unlock(&console_mutex);
}
// Task to compute fibonacci numbers
// It's more efficient to use an iterative algorithm, but
// the recursive algorithm takes longer and is more interesting
// than sleeping for X seconds to show parrallelism
class FibTask : public Task {
public:
FibTask(int n) : Task(), _n(n) {}
~FibTask() {
// Debug prints
pthread_mutex_lock(&console_mutex);
printf("tid(%d) - fibd(%d) being deleted\n", pthread_self(), _n);
pthread_mutex_unlock(&console_mutex);
}
virtual void run() {
// Note: it's important that this isn't contained in the console mutex lock
long long val = innerFib(_n);
// Show results
pthread_mutex_lock(&console_mutex);
printf("Fibd %d = %lld\n",_n, val);
pthread_mutex_unlock(&console_mutex);
// The following won't work in parrallel:
// pthread_mutex_lock(&console_mutex);
// printf("Fibd %d = %lld\n",_n, innerFib(_n));
// pthread_mutex_unlock(&console_mutex);
}
virtual void showTask() {
// More debug printing
pthread_mutex_lock(&console_mutex);
printf("thread %d computing fibonacci %d\n", pthread_self(), _n);
pthread_mutex_unlock(&console_mutex);
}
private:
// Slow computation of fibonacci sequence
// To make things interesting, and perhaps imporove load balancing, these
// inner computations could be added to the task queue
// Ideally set a lower limit on when that's done
// (i.e. don't create a task for fib(2)) because thread overhead makes it
// not worth it
long long innerFib(long long n) {
if (n<=1) { return 1; }
return innerFib(n-1) + innerFib(n-2);
}
long long _n;
};
int main(int argc, char *argv[])
{
// Create a thread pool
ThreadPool *tp = new ThreadPool(10);
// Create work for it
/*for (int i=0;i<100; ++i) {
int rv = rand() % 40 + 1;
showTask(rv);
tp->addTask(new FibTask(rv));
}*/
delete tp;
printf("\n\n\n\n\nDone with all work!\n");
}

The design is more or less OK-ish but implementationwise it contains several things that are a bit overcomplicated and may introduce instabilities. I guess you prog deadlocks when you comment out the for loop because you should use pthread_cond_broadcast instead of pthread_cond_signal in your WorkQueue::finish() method.
Note: I usually implemented threadpool termination by placing NUM_THREADS number of NULL items into the workqueue and I set a finished flag only to be able to check something in my addTask() method because after finish() I usually don't let adding new tasks and I return with false from addTask() or sometimes I assert.
Another note: Its best to encapsulate threads into classes, that has several benefits and makes proting to multiple platforms easier.
There may be other bugs too as I haven't executed your program, just ran through your code.
EDIT: Here is a reworked version, I issued some modifications to your code but I don't guarantee that it works. Fingers crossed... :-)
#include <stdio.h>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include <malloc.h>
#include <stdlib.h>
#include <assert.h>
// Reusable thread class
class Thread
{
public:
Thread()
{
state = EState_None;
handle = 0;
}
virtual ~Thread()
{
assert(state != EState_Started);
}
void start()
{
assert(state == EState_None);
// in case of thread create error I usually FatalExit...
if (pthread_create(&handle, NULL, threadProc, this))
abort();
state = EState_Started;
}
void join()
{
// A started thread must be joined exactly once!
// This requirement could be eliminated with an alternative implementation but it isn't needed.
assert(state == EState_Started);
pthread_join(handle, NULL);
state = EState_Joined;
}
protected:
virtual void run() = 0;
private:
static void* threadProc(void* param)
{
Thread* thread = reinterpret_cast<Thread*>(param);
thread->run();
return NULL;
}
private:
enum EState
{
EState_None,
EState_Started,
EState_Joined
};
EState state;
pthread_t handle;
};
// Base task for Tasks
// run() should be overloaded and expensive calculations done there
// showTask() is for debugging and can be deleted if not used
class Task {
public:
Task() {}
virtual ~Task() {}
virtual void run()=0;
virtual void showTask()=0;
};
// Wrapper around std::queue with some mutex protection
class WorkQueue
{
public:
WorkQueue() {
pthread_mutex_init(&qmtx,0);
// wcond is a condition variable that's signaled
// when new work arrives
pthread_cond_init(&wcond, 0);
}
~WorkQueue() {
// Cleanup pthreads
pthread_mutex_destroy(&qmtx);
pthread_cond_destroy(&wcond);
}
// Retrieves the next task from the queue
Task *nextTask() {
// The return value
Task *nt = 0;
// Lock the queue mutex
pthread_mutex_lock(&qmtx);
while (tasks.empty())
pthread_cond_wait(&wcond, &qmtx);
nt = tasks.front();
tasks.pop();
// Unlock the mutex and return
pthread_mutex_unlock(&qmtx);
return nt;
}
// Add a task
void addTask(Task *nt) {
// Lock the queue
pthread_mutex_lock(&qmtx);
// Add the task
tasks.push(nt);
// signal there's new work
pthread_cond_signal(&wcond);
// Unlock the mutex
pthread_mutex_unlock(&qmtx);
}
private:
std::queue<Task*> tasks;
pthread_mutex_t qmtx;
pthread_cond_t wcond;
};
// Thanks to the reusable thread class implementing threads is
// simple and free of pthread api usage.
class PoolWorkerThread : public Thread
{
public:
PoolWorkerThread(WorkQueue& _work_queue) : work_queue(_work_queue) {}
protected:
virtual void run()
{
while (Task* task = work_queue.nextTask())
task->run();
}
private:
WorkQueue& work_queue;
};
class ThreadPool {
public:
// Allocate a thread pool and set them to work trying to get tasks
ThreadPool(int n) {
printf("Creating a thread pool with %d threads\n", n);
for (int i=0; i<n; ++i)
{
threads.push_back(new PoolWorkerThread(workQueue));
threads.back()->start();
}
}
// Wait for the threads to finish, then delete them
~ThreadPool() {
finish();
}
// Add a task
void addTask(Task *nt) {
workQueue.addTask(nt);
}
// Asking the threads to finish, waiting for the task
// queue to be consumed and then returning.
void finish() {
for (size_t i=0,e=threads.size(); i<e; ++i)
workQueue.addTask(NULL);
for (size_t i=0,e=threads.size(); i<e; ++i)
{
threads[i]->join();
delete threads[i];
}
threads.clear();
}
private:
std::vector<PoolWorkerThread*> threads;
WorkQueue workQueue;
};
// stdout is a shared resource, so protected it with a mutex
static pthread_mutex_t console_mutex = PTHREAD_MUTEX_INITIALIZER;
// Debugging function
void showTask(int n) {
pthread_mutex_lock(&console_mutex);
pthread_mutex_unlock(&console_mutex);
}
// Task to compute fibonacci numbers
// It's more efficient to use an iterative algorithm, but
// the recursive algorithm takes longer and is more interesting
// than sleeping for X seconds to show parrallelism
class FibTask : public Task {
public:
FibTask(int n) : Task(), _n(n) {}
~FibTask() {
// Debug prints
pthread_mutex_lock(&console_mutex);
printf("tid(%d) - fibd(%d) being deleted\n", (int)pthread_self(), (int)_n);
pthread_mutex_unlock(&console_mutex);
}
virtual void run() {
// Note: it's important that this isn't contained in the console mutex lock
long long val = innerFib(_n);
// Show results
pthread_mutex_lock(&console_mutex);
printf("Fibd %d = %lld\n",(int)_n, val);
pthread_mutex_unlock(&console_mutex);
// The following won't work in parrallel:
// pthread_mutex_lock(&console_mutex);
// printf("Fibd %d = %lld\n",_n, innerFib(_n));
// pthread_mutex_unlock(&console_mutex);
// this thread pool implementation doesn't delete
// the tasks so we perform the cleanup here
delete this;
}
virtual void showTask() {
// More debug printing
pthread_mutex_lock(&console_mutex);
printf("thread %d computing fibonacci %d\n", (int)pthread_self(), (int)_n);
pthread_mutex_unlock(&console_mutex);
}
private:
// Slow computation of fibonacci sequence
// To make things interesting, and perhaps imporove load balancing, these
// inner computations could be added to the task queue
// Ideally set a lower limit on when that's done
// (i.e. don't create a task for fib(2)) because thread overhead makes it
// not worth it
long long innerFib(long long n) {
if (n<=1) { return 1; }
return innerFib(n-1) + innerFib(n-2);
}
long long _n;
};
int main(int argc, char *argv[])
{
// Create a thread pool
ThreadPool *tp = new ThreadPool(10);
// Create work for it
for (int i=0;i<100; ++i) {
int rv = rand() % 40 + 1;
showTask(rv);
tp->addTask(new FibTask(rv));
}
delete tp;
printf("\n\n\n\n\nDone with all work!\n");
}

I think you are having a race condition there...
When you remove the for loop, the pool is destructed as soon as it gets created so there is no time for the threads to start waiting on the queue. Try putting a sleep there and you'll see.
I implemented a threadpool library, which is used widely among all our services, so here come some advices:
You are using C++, so there's no need to use pthreads, just use boost, or std:thread if available
Don't signal, push empty tasks instead (pushing a task requires to signal, of course)
Use boost::function or std:function instead of a base class
Cope with spurious wake-ups (you code doesn't seem to handle them)
pthread_cond_signal wakes-up only one thread, you must use pthread_cond_broadcast if you want to notify them all, that said, I'd recommend, again, to stick to boost's conditions (#pasztorpisti got it rigth here, he's got my upvote)

Related

Self terminating thread. Use join or detach

I have a state machine being processed within a std::thread. This state machine initializes a network connection, processes data, and upon the receipt of a certain message, needs to shut itself down. Using join in this fashion triggers the 'abort() has been called' exception. Is this one of the cases where a detached thread is appropriate.
#include <iostream>
#include <thread>
#include <atomic>
#include <memory>
class ThreadExample
{
public:
ThreadExample()
{
StartThread();
}
void StartThread()
{
//start thread;
run_thread = true;
the_thread = std::thread(&ThreadExample::ThreadFunction, this);
}
void ThreadFunction()
{
while (run_thread)
{
if (correct_message_found)
ShutdownThread();
else
ProcessMessage(); //example code to imitate network processing
//arbitrary wait. not relevant to the problem
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
//read in by some network connection
void ProcessMessage(/*some message data*/)
{
static int counter = 0;
if (counter == 3)
{
correct_message_found = true;
}
else
{
std::cout << "Waiting for the right message\n";
counter++;
}
}
void ShutdownThread()
{
run_thread = false;
if (the_thread.joinable())
the_thread.join();
}
private:
std::thread the_thread;
std::atomic_bool run_thread;
bool correct_message_found = false;
};
int main()
{
auto example = std::make_unique<ThreadExample>();
int data;
std::cin >> data;
}
The correct way to terminate a thread from inside itself is to simply return from the function the thread is executing:
void ThreadFunction()
{
while (run_thread)
{
if (correct_message_found)
return;
else
ProcessMessage(); //example code to imitate network processing
//arbitrary wait. not relevant to the problem
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
Calling join from within the thread that is supposed to be joined is an error, see the first error condition: https://en.cppreference.com/w/cpp/thread/thread/join
join means "wait for the given thread to finish, then continue on". You are telling a thread to wait until it itself is finished. So it can only end once it has already ended, which is clearly contradictory.
Where you should call join is in the destructor of ThreadExample. ThreadFunction uses members of ThreadExample, and ThreadExample also owns the std::thread object, so ThreadExample cannot be allowed to die while the thread is still running. In the code you show, you would run into that problem if you input something before the thread is done: ThreadExample is then destroyed, and with it the std::thread object living inside. If a std::thread is destroyed while joinable (i.e. with a non-detached thread still running) then std::terminate is called:
https://en.cppreference.com/w/cpp/thread/thread/%7Ethread

A semaphore implmentation with Peterson's N process algorithm

I need feedback on my code for following statement, am I on right path?
Problem statement:
a. Implement a semaphore class that has a private int and three public methods: init, wait and signal. The wait and signal methods should behave as expected from a semaphore and must use Peterson's N process algorithm in their implementation.
b. Write a program that creates 5 threads that concurrently update the value of a shared integer and use an object of semaphore class created in part a) to ensure the correctness of the concurrent updates.
Here is my working program:
#include <iostream>
#include <pthread.h>
using namespace std;
pthread_mutex_t mid; //muted id
int shared=0; //global shared variable
class semaphore {
int counter;
public:
semaphore(){
}
void init(){
counter=1; //initialise counter 1 to get first thread access
}
void wait(){
pthread_mutex_lock(&mid); //lock the mutex here
while(1){
if(counter>0){ //check for counter value
counter--; //decrement counter
break; //break the loop
}
}
pthread_mutex_unlock(&mid); //unlock mutex here
}
void signal(){
pthread_mutex_lock(&mid); //lock the mutex here
counter++; //increment counter
pthread_mutex_unlock(&mid); //unlock mutex here
}
};
semaphore sm;
void* fun(void* id)
{
sm.wait(); //call semaphore wait
shared++; //increment shared variable
cout<<"Inside thread "<<shared<<endl;
sm.signal(); //call signal to semaphore
}
int main() {
pthread_t id[5]; //thread ids for 5 threads
sm.init();
int i;
for(i=0;i<5;i++) //create 5 threads
pthread_create(&id[i],NULL,fun,NULL);
for(i=0;i<5;i++)
pthread_join(id[i],NULL); //join 5 threads to complete their task
cout<<"Outside thread "<<shared<<endl;//final value of shared variable
return 0;
}
You need to release the mutex while spinning in the wait loop.
The test happens to work because the threads very likely run their functions start to finish before there is any context switch, and hence each one finishes before the next one even starts. So you have no contention over the semaphore. If you did, they'd get stuck with one waiter spinning with the mutex held, preventing anyone from accessing the counter and hence release the spinner.
Here's an example that works (though it may still have an initialization race that causes it to sporadically not launch correctly). It looks more complicated, mainly because it uses the gcc built-in atomic operations. These are needed whenever you have more than a single core, since each core has its own cache. Declaring the counters 'volatile' only helps with compiler optimization - for what is effectively SMP, cache consistency requires cross-processor cache invalidation, which means special processor instructions need to be used. You can try replacing them with e.g. counter++ and counter-- (and same for 'shared') - and observe how on a multi-core CPU it won't work. (For more details on the gcc atomic ops, see https://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/_005f_005fatomic-Builtins.html)
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
class semaphore {
pthread_mutex_t lock;
int32_t counter;
public:
semaphore() {
init();
}
void init() {
counter = 1; //initialise counter 1 to get first access
}
void spinwait() {
while (true) {
// Spin, waiting until we see a positive counter
while (__atomic_load_n(&counter, __ATOMIC_SEQ_CST) <= 0)
;
pthread_mutex_lock(&lock);
if (__atomic_load_n(&counter, __ATOMIC_SEQ_CST) <= 0) {
// Someone else stole the count from under us or it was
// a fluke - keep trying
pthread_mutex_unlock(&lock);
continue;
}
// It's ours
__atomic_fetch_add(&counter, -1, __ATOMIC_SEQ_CST);
pthread_mutex_unlock(&lock);
return;
}
}
void signal() {
pthread_mutex_lock(&lock); //lock the mutex here
__atomic_fetch_add(&counter, 1, __ATOMIC_SEQ_CST);
pthread_mutex_unlock(&lock); //unlock mutex here
}
};
enum {
NUM_TEST_THREADS = 5,
NUM_BANGS = 1000
};
// Making semaphore sm volatile would be complicated, because the
// pthread_mutex library calls don't expect volatile arguments.
int shared = 0; // Global shared variable
semaphore sm; // Semaphore protecting shared variable
volatile int num_workers = 0; // So we can wait until we have N threads
void* fun(void* id)
{
usleep(100000); // 0.1s. Encourage context switch.
const int worker = (intptr_t)id + 1;
printf("Worker %d ready\n", worker);
// Spin, waiting for all workers to be in a runnable state. These printouts
// could be out of order.
++num_workers;
while (num_workers < NUM_TEST_THREADS)
;
// Go!
// Bang on the semaphore. Odd workers increment, even decrement.
if (worker & 1) {
for (int n = 0; n < NUM_BANGS; ++n) {
sm.spinwait();
__atomic_fetch_add(&shared, 1, __ATOMIC_SEQ_CST);
sm.signal();
}
} else {
for (int n = 0; n < NUM_BANGS; ++n) {
sm.spinwait();
__atomic_fetch_add(&shared, -1, __ATOMIC_SEQ_CST);
sm.signal();
}
}
printf("Worker %d done\n", worker);
return NULL;
}
int main() {
pthread_t id[NUM_TEST_THREADS]; //thread ids
// create test worker threads
for(int i = 0; i < NUM_TEST_THREADS; i++)
pthread_create(&id[i], NULL, fun, (void*)((intptr_t)(i)));
// join threads to complete their task
for(int i = 0; i < NUM_TEST_THREADS; i++)
pthread_join(id[i], NULL);
//final value of shared variable. For an odd number of
// workers this is the loop count, NUM_BANGS
printf("Test done. Final value: %d\n", shared);
const int expected = (NUM_TEST_THREADS & 1) ? NUM_BANGS : 0;
if (shared == expected) {
puts("PASS");
} else {
printf("Value expected was: %d\nFAIL\n", expected);
}
return 0;
}

Threaded timer, interrupting a sleep (stopping it)

I'm wanting a reasonably reliable threaded timer, so I've written a timer object that fires a std::function on a thread. I would like to give this timer the ability to stop before it gets to the next tick; something you can't do with ::sleep (at least I don't think you can).
So what I've done is put a condition variable on a mutex. If the condition times out, I fire the event. If the condition is signalled the thread is exited. So the Stop method needs to be able to get the thread to stop and/or interrupt its wait, which I think is what it's doing right now.
There are problems with this however. Sometimes the thread isn't joinable() and sometimes the condition is signalled after its timeout but before it's put into its wait state.
How can I improve this and make it robust?
The following is a full repo. The wait is 10 seconds here but the program should terminate immediately as the Foo is created and then immediately destroyed. It does sometimes but mostly it does not.
#include <atomic>
#include <thread>
#include <future>
#include <sstream>
#include <chrono>
#include <iostream>
class Timer
{
public:
Timer() {}
~Timer()
{
Stop();
}
void Start(std::chrono::milliseconds const & interval, std::function<void(void)> const & callback)
{
Stop();
thread = std::thread([=]()
{
for(;;)
{
auto locked = std::unique_lock<std::mutex>(mutex);
auto result = terminate.wait_for(locked, interval);
if (result == std::cv_status::timeout)
{
callback();
}
else
{
return;
}
}
});
}
void Stop()
{
terminate.notify_one();
if(thread.joinable())
{
thread.join();
}
}
private:
std::thread thread;
std::mutex mutex;
std::condition_variable terminate;
};
class Foo
{
public:
Foo()
{
timer = std::make_unique<Timer>();
timer->Start(std::chrono::milliseconds(10000), std::bind(&Foo::Callback, this));
}
~Foo()
{
}
void Callback()
{
static int count = 0;
std::ostringstream o;
std::cout << count++ << std::endl;
}
std::unique_ptr<Timer> timer;
};
int main(void)
{
{
Foo foo;
}
return 0;
}
See my comment. You forgot to implement the state of the thing the thread is waiting for, leaving the mutex nothing to protect and the thread nothing to wait for. Condition variables are stateless -- your code must track the state of the thing whose change you're notifying the thread about.
Here's the code fixed. Notice that the mutex protects stop, and stop is the thing the thread is waiting for.
class Timer
{
public:
Timer() {}
~Timer()
{
Stop();
}
void Start(std::chrono::milliseconds const & interval,
std::function<void(void)> const & callback)
{
Stop();
{
auto locked = std::unique_lock<std::mutex>(mutex);
stop = false;
}
thread = std::thread([=]()
{
auto locked = std::unique_lock<std::mutex>(mutex);
while (! stop) // We hold the mutex that protects stop
{
auto result = terminate.wait_for(locked, interval);
if (result == std::cv_status::timeout)
{
callback();
}
}
});
}
void Stop()
{
{
// Set the predicate
auto locked = std::unique_lock<std::mutex>(mutex);
stop = true;
}
// Tell the thread the predicate has changed
terminate.notify_one();
if(thread.joinable())
{
thread.join();
}
}
private:
bool stop; // This is the thing the thread is waiting for
std::thread thread;
std::mutex mutex;
std::condition_variable terminate;
};

Inner class and initialisation

I have a class defined like this: This is not all complete and probably won't compile.
class Server
{
public:
Server();
~Server();
class Worker
{
public:
Worker(Server& server) : _server(server) { }
~Worker() { }
void Run() { }
void Stop() { }
private:
Server& _server;
}
void Run()
{
while(true) {
// do work
}
}
void Stop()
{
// How do I stop the thread?
}
private:
std::vector<Worker> _workers;
};
My question is, how do I initialize the workers array passing in the outer class named Server.
What I want is a vector of worker threads. Each worker thread has it's own state but can access some other shared data (not shown). Also, how do I create the threads. Should they be created when the class object is first created or externally from a thread_group.
Also, how do I go about shutting down the threads cleanly and safely?
EDIT:
It seems that I can initialize Worker like this:
Server::Server(int thread_count)
: _workers(thread_count), Worker(*this)), _thread_count(thread_count) { }
And I'm currently doing this in Server::Run to create the threads.
boost::thread_group _threads; // a Server member variable
Server::Run(){
for (int i = 0; i < _thread_count; i++)
_threads.create_thread(boost::bind(&Server::Worker::Run, _workers(i)));
// main thread.
while(1) {
// Do stuff
}
_threads.join_all();
}
Does anyone see any problems with this?
And how about safe shutdown?
EDIT:
One problem I have found with it is that the Worker objects don't seem to get constructed!
oops. Yes they do I need a copy constructor on the Worker class.
But oddly, creating the threads results in the copy constructor for Worker being called multiple times.
I have done it with pure WINAPI, look:
#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <vector>
using namespace std;
class Server
{
public:
class Worker
{
int m_id;
DWORD m_threadId;
HANDLE m_threadHandle;
bool m_active;
friend Server;
public:
Worker (int id)
{
m_id = id;
m_threadId = 0;
m_threadHandle = 0;
m_active = true;
}
static DWORD WINAPI Run (LPVOID lpParam)
{
Worker* p = (Worker*) lpParam; // it's needed because of the static modifier
while (p->m_active)
{
printf ("I'm a thread #%i\n", p->m_id);
Sleep (1000);
}
return 0;
}
void Stop ()
{
m_active = false;
}
};
Server ()
{
m_workers = new vector <Worker*> ();
m_count = 0;
}
~Server ()
{
delete m_workers;
}
void Run ()
{
puts ("Server is run");
}
void Stop ()
{
while (m_count > 0)
RemoveWorker ();
puts ("Server has been stopped");
}
void AddWorker ()
{
HANDLE h;
DWORD threadId;
Worker* n = new Worker (m_count ++);
m_workers->push_back (n);
h = CreateThread (NULL, 0, Worker::Run, (VOID*) n, CREATE_SUSPENDED, &threadId);
n->m_threadHandle = h;
n->m_threadId = threadId;
ResumeThread (h);
}
void RemoveWorker ()
{
HANDLE h;
DWORD threadId;
if (m_count <= 0)
return;
Worker* n = m_workers->at (m_count - 1);
m_workers->pop_back ();
n->Stop ();
TerminateThread (n->m_threadHandle, 0);
m_count --;
delete n;
}
private:
int m_count;
vector <Worker*>* m_workers;
};
int main (void)
{
Server a;
int com = 1;
a.Run ();
while (com)
{
if (kbhit ())
{
switch (getch ())
{
case 27: // escape key code
com = 0;
break;
case 'a': // add worker
a.AddWorker ();
break;
case 'r': // remove worker
a.RemoveWorker ();
break;
}
}
}
a.Stop ();
return 0;
}
There are no synchronization code here, because I haven't enougth time to do it... But I wish it will help you =)
Have you looked at boost asio at all? It looks like it could be a good fit for what you are trying to do. Additionally you can call boost asio's io_service's run (similar to your Run method) from many threads i.e. you can process your IO in many threads. Also of interest could be http://think-async.com/Asio/Recipes for an asio based thread-pool.
Have a look at the asio examples. Perhaps they offer an alternative way of handling what you are trying to do. Esp. have a look at how a clean shutdown is accomplished.

Returning values from pthread asynchronously at regular intervals

The main() function creates a thread that is supposed to live until the user wishes to exit the program. The thread needs to return values to the main functions at periodic intervals. I tried doing something like this, but hasn't worked well -
std::queue<std::string> q;
void start_thread(int num)
{
std::string str;
//Do some processing
q.push(str);
}
int main()
{
//Thread initialization
int i;
//Start thread
pthread_create(&m_thread,NULL,start_thread,static_cast<void *>i);
while(true)
{
if(q.front())
{
std::cout<<q.front();
return 0;
}
}
//Destroy thread.....
return 0;
}
Any suggestions?
It is not safe to read and write from STL containers concurrently. You need a lock to synchronize access (see pthread_mutex_t).
Your thread pushes a single value into the queue. You seem to be expecting periodic values, so you'll want to modify start_thread to include a loop that calls queue.push.
The return 0; in the consumer loop will exit main() when it finds a value in the queue. You'll always read a single value and exit your program. You should remove that return.
Using if (q.front()) is not the way to test if your queue has values (front assumes at least one element exists). Try if (!q.empty()).
Your while(true) loop is gonna spin your processor somethin' nasty. You should look at condition variables to wait for values in the queue in a nice manner.
try locking a mutex before calling push() / front() on the queue.
Here is a working example of what it looks like you were trying to accomplish:
#include <iostream>
#include <queue>
#include <vector>
#include <semaphore.h>
#include <pthread.h>
struct ThreadData
{
sem_t sem;
pthread_mutex_t mut;
std::queue<std::string> q;
};
void *start_thread(void *num)
{
ThreadData *td = reinterpret_cast<ThreadData *>(num);
std::vector<std::string> v;
std::vector<std::string>::iterator i;
// create some data
v.push_back("one");
v.push_back("two");
v.push_back("three");
v.push_back("four");
i = v.begin();
// pump strings out until no more data
while (i != v.end())
{
// lock the resource and put string in the queue
pthread_mutex_lock(&td->mut);
td->q.push(*i);
pthread_mutex_unlock(&td->mut);
// signal activity
sem_post(&td->sem);
sleep(1);
++i;
}
// signal activity
sem_post(&td->sem);
}
int main()
{
bool exitFlag = false;
pthread_t m_thread;
ThreadData td;
// initialize semaphore to empty
sem_init(&td.sem, 0, 0);
// initialize mutex
pthread_mutex_init(&td.mut, NULL);
//Start thread
if (pthread_create(&m_thread, NULL, start_thread, static_cast<void *>(&td)) != 0)
{
exitFlag = true;
}
while (!exitFlag)
{
if (sem_wait(&td.sem) == 0)
{
pthread_mutex_lock(&td.mut);
if (td.q.empty())
{
exitFlag = true;
}
else
{
std::cout << td.q.front() << std::endl;
td.q.pop();
}
pthread_mutex_unlock(&td.mut);
}
else
{
// something bad happened
exitFlag = true;
}
}
return 0;
}