I would like to implement a thread class using pthread.
Of course I would like to have different starting routines for each thread I'm creating.
pthread_create tho allows only a static function as starting routine, so it can't be instantiated.
Is there a way to allow that or is it better to use a struct to handle my threads ?
This is the code I wrote sofar:
class thread {
string name;
pthread_t id;
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_attr_t attr;
public:
thread (string t_name);
static void* start(void*);
int id_get();
private:
};
thread::thread (string t_name)
{
name = t_name;
pthread_attr_init(&attr);
int stacksize = sizeof(double) * TH_STACK_SIZE * 30;
pthread_attr_setstacksize(&attr, stacksize);
int rc = pthread_create (&id, &attr, &start, NULL);
cout << "return_code: " << rc << endl;
cout << id;
}
void* thread::start(void*)
{
while(1){
cout << "here";
pthread_exit(NULL);
}
}
int thread::id_get()
{
return id;
}
and my test main:
int main(void) {
cout << "Creating threads" << endl;
thread test1("first");
thread test2("second");
pthread_join(test1.id_get(),NULL);
pthread_join(test2.id_get(),NULL);
return 0;
}
I would like to have different starting routines for each thread I'm
creating.
Back when I used posix thread, (I now use std::thread), I used a 'two-step' entry mechanism. At the (small) cost of these two steps, every class could easily have its own thread.
I always keep these entry methods private.
class Foo_t
{
// ... etc
private:
static void* threadEntry(void* ptr);
void* threadEntry2(void); // thread actions in an object method
// ... etc
}
Because these are private, the class has some public method to create the posix thread, typically something like:
void Foo_t::startApp()
{
// ... etc
int pcStat = m_Thread.create(Foo_t::threadEntry, this);
// this 2 parameter method of my thread wrapper class
// invoked the 4 parameter "::pthread_create(...)".
// The 'this' param is passed into the 4th parameter, called arg.
dtbAssert(0 == pcStat)(m_nodeId)(pcStat)(errno);
// ...
}
Note the second parameter, 'this', to m_Thread.create().
The thread would start in the static method:
void* Foo_t::threadEntry(void* a_ptr)
{
dtbAssert(a_ptr != 0);
Foo_t* a_foo = static_cast<Foo_t*>(a_ptr);
void* retVal = a_foo->threadEntry2();
return(retVal);
}
Here, the void* parameter is filled in with the 'this' pointer of the class instance, and then static_cast back to what we need, a Foo_t*. Remember, this method is private, so only startApp() would create a thread.
Note that threadEntry() invokes an actual method of the class instance called:
void* Foo_t::threadEntry2(void)
{
DBG("Thread %2d (id=%lx): sems %p/%p, "
"Entering sem controlled critical region\n", ...);
// ... start thread work
}
And from here, any method of the instance is available.
So, what next. There are so many ways to proceed to different thread routines.
Consider adding a parameter to startApp:
void Foo_t::startApp(int select);
The 'int select' and a switch/case statement could run a unique threadEntry().
Perhaps the 'int select' could be installed (in the instance) so that a later switch/case in threadEntry() could run a unique method or threadEntry2_x().
Or perhaps the switch/case might be installed in threadEntry2().
Consider that the startApp parameter might be a method pointer.
void Foo_t::startApp(<method pointer>);
The method pointer could be (somewhat more directly) invoked instead of the 'fixed' name threadEntry2().
The above are small issues.
Mutex and having more than 1 thread running in an instance are bigger issues.
I have indeed had multiple threads 'running-around' in a single class instance. For that I used critical sections, under mutex or some other guard mechanisms. std::mutex is convenient, and works with 'Posix' threads, but, on Ubuntu, I often use a Posix Process Semaphore, set to Local mode (unnamed, unshared). PPLSem_t is efficient and fits into 4 one line methods wrapped in a small class.
pthread_create tho allows only a static function as starting routine,
so it can't be instantiated.
There is no difficulty instantiating an instance of a class containing a static method. I'm not sure what you mean in this statement / context.
Review the approach I have detailed above, and you should quickly get to functioning Posix threads in your class instance.
Remember to check on stack usage and how much ram is available on your ARM system. The Ubuntu default stack size is 8 MBytes. Perhaps your ARM provides stack size control.
If you have POSIX threads availble, std::thread will be available for any C++ compiler supporting the current standard (since c++11).
So basically you don't need to roll your own thread class for your cross compiled target (e.g. GCC supports that since version 4.9 or so).
But in general your approach is correct. To make it applicable for various classes you can simmply make the thread class a template:
template<typename T>
class thread {
string name;
pthread_t id;
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_attr_t attr;
public:
thread (string t_name, T& runnable);
static void* start(void*);
int id_get();
T& runnable_;
};
And implement the constructor and start() function as follows:
template<typename T>
thread<T>::thread (string t_name)
: name(t_name)
, runnable_(runnable)
{
pthread_attr_init(&attr);
int stacksize = sizeof(double) * TH_STACK_SIZE * 30;
pthread_attr_setstacksize(&attr, stacksize);
int rc = pthread_create (&id, &attr, &start, this);
// ^^^^
cout << "return_code: " << rc << endl;
cout << id;
}
template<typename T>
void* thread<T>::start(void* pThis) {
thread<T>* realThis = reinterpret_cast<thread<T>*>(pThis);
(realThis->runnable)_.start();
pthread_exit(NULL);
}
The thread class can be used then like follows:
struct MyRunnable {
MyRunnable(/* Whatever parameters needed */)
: /* Whatever needs to be initialized */ {
}
void start() {
/* Full access to all class member variables */
}
}
int main() {
MyRunnable run(/* Whatever parameters needed */);
thread<MyRunnable> t("TheTreadName",run); // start() will execute here
// do concurrent stuff
t.join();
}
I just would choose a different name as thread to avoid any clashes with the c++ standard library.
Related
I'm having the hardest time trying to wrap my head around how to allow threads to signal each other.
My design:
The main function creates a single master thread that coordinates a bunch of other worker threads. The main function also creates the workers because the worker threads spawn and exit at intervals programmed in the main. The master thread needs to be able to signal these worker threads and signal_broadcast them all as well as the worker threads have to signal the master back (pthread_cond_signal). Since each thread needs a pthread_mutex and pthread_cond I made a Worker class and a Master class with these variables. Now this is where I am stuck. C++ does not allow you to pass member functions as the pthread_create(...) handler so I had to make a static handler inside and pass a pointer to itself to reinterpret_cast it to use its class data...
void Worker::start() {
pthread_create(&thread, NULL, &Worker::run, this);
}
void* Worker::run(void *ptr) {
Worker* data = reinterpret_cast<Worker*>(ptr);
}
The problem I have with this, probably wrong, setup is that when I passed in an array of worker pointers to the Master thread it signals a different reference of worker because I think the cast did some sort of copy. So I tried static_cast and same behavior.
I just need some sort of design where the Master and workers can pthread_cond_wait(...) and pthread_cond_signal(...) each other.
Edit 1
Added:
private:
Worker(const Worker&);
Still not working.
Edit Fixed the potential race in all versions:
1./1b Employs a sempaaphore built from a (mutex+condition+counter) as outlined in C++0x has no semaphores? How to synchronize threads?
2. uses a 'reverse' wait to ensure that a signal got ack-ed by the intended worker
I'd really suggest to use c++11 style <thread> and <condition_variable> to achieve this.
I have two (and a half) demonstations. They each assume you have 1 master that drives 10 workers. Each worker awaits a signal before it does it's work.
We'll use std::condition_variable (which works in conjunction with a std::mutex) to do the signaling. The difference between the first and second version will be the way in which the signaling is done:
1. Notifying any worker, one at a time:
1b. With a worker struct
2. Notifying all threads, coordinating which recipient worker is to respond
1. Notifying any worker, one at a time:
This is the simplest to do, because there's little coordination going on:
#include <vector>
#include <thread>
#include <mutex>
#include <algorithm>
#include <iostream>
#include <condition_variable>
using namespace std;
class semaphore
{ // see https://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads
std::mutex mx;
std::condition_variable cv;
unsigned long count;
public:
semaphore() : count() {}
void notify();
void wait();
};
static void run(int id, struct master& m);
struct master
{
mutable semaphore sem;
master()
{
for (int i = 0; i<10; ++i)
threads.emplace_back(run, i, ref(*this));
}
~master() {
for(auto& th : threads) if (th.joinable()) th.join();
std::cout << "done\n";
}
void drive()
{
// do wakeups
for (unsigned i = 0; i<threads.size(); ++i)
{
this_thread::sleep_for(chrono::milliseconds(rand()%100));
sem.notify();
}
}
private:
vector<thread> threads;
};
static void run(int id, master& m)
{
m.sem.wait();
{
static mutex io_mx;
lock_guard<mutex> lk(io_mx);
cout << "signaled: " << id << "\n";
}
}
int main()
{
master instance;
instance.drive();
}
/// semaphore members
void semaphore::notify()
{
lock_guard<mutex> lk(mx);
++count;
cv.notify_one();
}
void semaphore::wait()
{
unique_lock<mutex> lk(mx);
while(!count)
cv.wait(lk);
--count;
}
1b. With a worker struct
Note, if you had worker classes with worker::run a non-static member function, you can do the same with minor modifications:
struct worker
{
worker(int id) : id(id) {}
void run(master& m) const;
int id;
};
// ...
struct master
{
// ...
master()
{
for (int i = 0; i<10; ++i)
workers.emplace_back(i);
for (auto& w: workers)
threads.emplace_back(&worker::run, ref(w), ref(*this));
}
// ...
void worker::run(master& m) const
{
m.sem.wait();
{
static mutex io_mx;
lock_guard<mutex> lk(io_mx);
cout << "signaled: " << id << "\n";
}
}
A caveat
cv.wait() could suffer spurious wake-ups, in which the condition variable wasn't atually raised (e.g. in the event of OS signal handlers). This is a common thing to happen with condition variables on any platfrom.
The following approach fixes this:
2. Notifying all threads, coordinating which recipient worker
Use a flag to signal which thread was intended to receive the signal:
struct master
{
mutable mutex mx;
mutable condition_variable cv;
int signaled_id; // ADDED
master() : signaled_id(-1)
{
Let's pretend that driver got a lot more interesting and wants to signal all workers in a specific (random...) order:
void drive()
{
// generate random wakeup order
vector<int> wakeups(10);
iota(begin(wakeups), end(wakeups), 0);
random_shuffle(begin(wakeups), end(wakeups));
// do wakeups
for (int id : wakeups)
{
this_thread::sleep_for(chrono::milliseconds(rand()%1000));
signal(id);
}
}
private:
void signal(int id) // ADDED id
{
unique_lock<mutex> lk(mx);
std::cout << "signaling " << id << "\n";
signaled_id = id; // ADDED put it in the shared field
cv.notify_all();
cv.wait(lk, [&] { return signaled_id == -1; });
}
Now all we have to do is make sure that the receiving thread checks that it's id matches:
m.cv.wait(lk, [&] { return m.signaled_id == id; });
m.signaled_id = -1;
m.cv.notify_all();
This puts an end to spurious wake-ups.
Full code listings/live demos:
1. notify_one.cpp http://coliru.stacked-crooked.com/view?id=c968f8cffd57afc2a0c6777105203f85-03e740563a9d9c6bf97614ba6099fe92
1b. id. with worker struct: http://coliru.stacked-crooked.com/view?id=7bd224c42130a0461b0c894e0b7c74ae-03e740563a9d9c6bf97614ba6099fe92
2. notify_all.cpp http://coliru.stacked-crooked.com/view?id=1d3145ccbb93c1bec03b232d372277b8-03e740563a9d9c6bf97614ba6099fe92
It is not clear what your exact circumstances are, but it seems like you are using a container to hold your "Worker" instances that are created in main, and passing them to your "Master". If this is the case, there are a few remedies available to you. You need to pick one that is appropriate to your implementation.
Pass a reference to the container in main to the Master.
Change the container to hold (smart) pointers to Workers.
Make the container part of "Master" itself, so that it doesn't need to be passed to it.
Implement a proper destructor, copy constructor, and assignment operator for your Worker class (in other words, obey the Rule of Three).
Technically speaking, since pthread_create() is a C API, the function pointer that is passed to it needs to have C linkage (extern "C"). You can't make a method of a C++ class have C linkage, so you should define an external function:
extern "C" { static void * worker_run (void *arg); }
class Worker { //...
};
static void * worker_run (void *arg) {
return Worker::run(arg);
}
My critical section code does not work!!!
Backgrounder.run IS able to modify MESSAGE_QUEUE g_msgQueue and LockSections destructor hadn't been called yet !!!
Extra code :
typedef std::vector<int> MESSAGE_LIST; // SHARED OBJECT .. MUST LOCK!
class MESSAGE_QUEUE : MESSAGE_LIST{
public:
MESSAGE_LIST * m_pList;
MESSAGE_QUEUE(MESSAGE_LIST* pList){ m_pList = pList; }
~MESSAGE_QUEUE(){ }
/* This class will be shared between threads that means any
* attempt to access it MUST be inside a critical section.
*/
void Add( int messageCode ){ if(m_pList) m_pList->push_back(messageCode); }
int getLast()
{
if(m_pList){
if(m_pList->size() == 1){
Add(0x0);
}
m_pList->pop_back();
return m_pList->back();
}
}
void removeLast()
{
if(m_pList){
m_pList->erase(m_pList->end()-1,m_pList->end());
}
}
};
class Backgrounder{
public:
MESSAGE_QUEUE* m_pMsgQueue;
static void __cdecl Run( void* args){
MESSAGE_QUEUE* s_pMsgQueue = (MESSAGE_QUEUE*)args;
if(s_pMsgQueue->getLast() == 0x45)printf("It's a success!");
else printf("It's a trap!");
}
Backgrounder(MESSAGE_QUEUE* pMsgQueue)
{
m_pMsgQueue = pMsgQueue;
_beginthread(Run,0,(void*)m_pMsgQueue);
}
~Backgrounder(){ }
};
int main(){
MESSAGE_LIST g_List;
CriticalSection crt;
ErrorHandler err;
LockSection lc(&crt,&err); // Does not work , see question #2
MESSAGE_QUEUE g_msgQueue(&g_List);
g_msgQueue.Add(0x45);
printf("%d",g_msgQueue.getLast());
Backgrounder back_thread(&g_msgQueue);
while(!kbhit());
return 0;
}
#ifndef CRITICALSECTION_H
#define CRITICALSECTION_H
#include <windows.h>
#include "ErrorHandler.h"
class CriticalSection{
long m_nLockCount;
long m_nThreadId;
typedef CRITICAL_SECTION cs;
cs m_tCS;
public:
CriticalSection(){
::InitializeCriticalSection(&m_tCS);
m_nLockCount = 0;
m_nThreadId = 0;
}
~CriticalSection(){ ::DeleteCriticalSection(&m_tCS); }
void Enter(){ ::EnterCriticalSection(&m_tCS); }
void Leave(){ ::LeaveCriticalSection(&m_tCS); }
void Try();
};
class LockSection{
CriticalSection* m_pCS;
ErrorHandler * m_pErrorHandler;
bool m_bIsClosed;
public:
LockSection(CriticalSection* pCS,ErrorHandler* pErrorHandler){
m_bIsClosed = false;
m_pCS = pCS;
m_pErrorHandler = pErrorHandler;
// 0x1AE is code prefix for critical section header
if(!m_pCS)m_pErrorHandler->Add(0x1AE1);
if(m_pCS)m_pCS->Enter();
}
~LockSection(){
if(!m_pCS)m_pErrorHandler->Add(0x1AE2);
if(m_pCS && m_bIsClosed == false)m_pCS->Leave();
}
void ForceCSectionClose(){
if(!m_pCS)m_pErrorHandler->Add(0x1AE3);
if(m_pCS){m_pCS->Leave();m_bIsClosed = true;}
}
};
/*
Safe class basic structure;
class SafeObj
{
CriticalSection m_cs;
public:
void SafeMethod()
{
LockSection myLock(&m_cs);
//add code to implement the method ...
}
};
*/
#endif
Two questions in one. I don't know about the first, but the critical section part is easy to explain. The background thread isn't trying to claim the lock and so, of course, is not blocked. You need to make the critical section object crt visible to the thread so that it can lock it.
The way to use this lock class is that each section of code that you want serialised must create a LockSection object and hold on to it until the end of the serialised block:
Thread 1:
{
LockSection lc(&crt,&err);
//operate on shared object from thread 1
}
Thread 2:
{
LockSection lc(&crt,&err);
//operate on shared object from thread 2
}
Note that it has to be the same critical section instance crt that is used in each block of code that is to be serialised.
This code has a number of problems.
First of all, deriving from the standard containers is almost always a poor idea. In this case you're using private inheritance, which reduces the problems, but doesn't eliminate them entirely. In any case, you don't seem to put the inheritance to much (any?) use anyway. Even though you've derived your MESSAGE_QUEUE from MESSAGE_LIST (which is actually std::vector<int>), you embed a pointer to an instance of a MESSAGE_LIST into MESSAGE_QUEUE anyway.
Second, if you're going to use a queue to communicate between threads (which I think is generally a good idea) you should make the locking inherent in the queue operations rather than requiring each thread to manage the locking correctly on its own.
Third, a vector isn't a particularly suitable data structure for representing a queue anyway, unless you're going to make it fixed size, and use it roughly like a ring buffer. That's not a bad idea either, but it's quite a bit different from what you've done. If you're going to make the size dynamic, you'd probably be better off starting with a deque instead.
Fourth, the magic numbers in your error handling (0x1AE1, 0x1AE2, etc.) is quite opaque. At the very least, you need to give these meaningful names. The one comment you have does not make the use anywhere close to clear.
Finally, if you're going to go to all the trouble of writing code for a thread-safe queue, you might as well make it generic so it can hold essentially any kind of data you want, instead of dedicating it to one specific type.
Ultimately, your code doesn't seem to save the client much work or trouble over using the Windows functions directly. For the most part, you've just provided the same capabilities under slightly different names.
IMO, a thread-safe queue should handle almost all the work internally, so that client code can use it about like it would any other queue.
// Warning: untested code.
// Assumes: `T::T(T const &) throw()`
//
template <class T>
class queue {
std::deque<T> data;
CRITICAL_SECTION cs;
HANDLE semaphore;
public:
queue() {
InitializeCriticalSection(&cs);
semaphore = CreateSemaphore(NULL, 0, 2048, NULL);
}
~queue() {
DeleteCriticalSection(&cs);
CloseHandle(semaphore);
}
void push(T const &item) {
EnterCriticalSection(&cs);
data.push_back(item);
LeaveCriticalSection(&cs);
ReleaseSemaphore(semaphore, 1, NULL);
}
T pop() {
WaitForSingleObject(semaphore, INFINITE);
EnterCriticalSection(&cs);
T item = data.front();
data.pop_front();
LeaveCriticalSection(&cs);
return item;
}
};
HANDLE done;
typedef queue<int> msgQ;
enum commands { quit, print };
void backgrounder(void *qq) {
// I haven't quite puzzled out what your background thread
// was supposed to do, so I've kept it really simple, executing only
// the two commands listed above.
msgQ *q = (msgQ *)qq;
int command;
while (quit != (command = q->pop()))
printf("Print\n");
SetEvent(done);
}
int main() {
msgQ q;
done = CreateEvent(NULL, false, false, NULL);
_beginthread(backgrounder, 0, (void*)&q);
for (int i=0; i<20; i++)
q.push(print);
q.push(quit);
WaitForSingleObject(done, INFINITE);
return 0;
}
Your background thread needs access to the same CriticalSection object and it needs to create LockSection objects to lock it -- the locking is collaborative.
You are trying to return the last element after popping it.
Here is the issue that I'm having with multithreading. The proc needs to be static which means the only way I see that 2 threads can communicate and share data is through the global scope. This does not seem very clean nor does it feel very OO. I know I can create a static proc function in a class but that's still static.
What I'd like to for example do is have thread procs in the class somehow so that ex: I could create an MD5 checksum class and have an array of these objects, each on their own thread checking its hash, while the UI thread is not impaired by this and another class could simply keep track of the handles and wait for multiple objects before saying "Complete" or something. How is this limitation usually overcome?
You cannot avoid using a static function if you want to start a thread there. You can however (using Windows) pass the this pointer as a parameter and use it on the other side to enter the class instance.
#include <windows.h>
class Threaded {
static DWORD WINAPI StaticThreadEntry(LPVOID me) {
reinterpret_cast<Threaded*>(me)->ThreadEntry();
return 0;
}
void ThreadEntry() {
// Stuff here.
}
public:
void DoSomething() {
::CreateThread(0, 0, StaticThreadEntry, this, 0, 0);
}
};
In C++, Boost.Thread solves the problem nicely. A thread is represented by a functor, meaning that the (non-static) operator() is the thread's entry point.
For example, a thread can be created like this:
// define the thread functor
struct MyThread {
MyThread(int& i) : i(i) {}
void operator()(){...}
private:
int& i;
};
// create the thread
int j;
boost::thread thr(MyThread(j));
by passing data to the thread functor's constructor, we can pass parameters to the thread without having to rely on globals. (In this case, the thread is given a reference to the integer j declared outside the thread.)
With other libraries or APIs, it's up to you to make the jump from a (typically static) entry point to sharing non-static data.
The thread function typically takes a (sometimes optional) parameter (often of type void*), which you can use to pass instance data to the thread.
If you use this to pass a pointer to some object to the thread, then the thread can simply cast the pointer back to the object type, and access the data, without having to rely on globals.
For example, (in pseudocode), this would have roughly the same effect as the Boost example above:
void MyThreadFunc(void* params) {
int& i = *(int*)params;
...
}
int j;
CreateThread(MyThreadFunc, &j);
Or the parameter can be a pointer to an object whose (non-static) member function you wish to call, allowing you to execute a class member function instead of a nonmember.
I'm not sure I understood well... I give it a try. Are you looking for thread local storage ?
Thread creation routines usually allow you to pass a parameter to the function which will run in a new thread. This is true for both Posix pthread_create(...) and Win32 CreateThread(...). Here is a an example using Pthreads:
void* func (void* arg) {
queue_t* pqueue = (queue_t*)arg;
// pull messages off the queue
val = queue_pull(pqueue);
return 0;
}
int main (int argc, char* argv[]) {
pthread_t thread;
queue_t queue = queue_init();
pthread_create(&thread, 0, func, &queue);
// push messages on the queue for the thread to process
queue_push(&queue, 123);
void* ignored;
pthread_join(&thread, &ignored);
return 0;
}
No statics anywhere. In a C++ program you could pass a pointer to an instance of a class.
I use C++ to implement a thread class. My code shows in the following.
I have a problem about how to access thread data.
In the class Thread, I create a thread use pthread_create() function. then it calls EntryPoint() function to start thread created. In the Run function, I want to access the mask variable, it always shows segment fault.
So, my question is whether the new created thread copy the data in original class? How to access the thread own data?
class Thread {
public:
int mask;
pthread_t thread;
Thread( int );
void start();
static void * EntryPoint (void *);
void Run();
};
Thread::Thread( int a) {
mask =a;
}
void Thread::Run() {
cout<<"thread begin to run" <<endl;
cout << mask <<endl; // it always show segmentfault here
}
void * Thread::EntryPoint(void * pthis) {
cout << "entry" <<endl;
Thread *pt = (Thread *) pthis;
pt->Run();
}
void Thread::start() {
pthread_create(&thread, NULL, EntryPoint, (void *)ThreadId );
pthread_join(thread, NULL);
}
int main() {
int input_array[8]={3,1,2,5,6,8,7,4};
Thread t1(1);
t1.start();
}
I'm not familiar with the libraries you're using, but how does EntryPoint know that pthis is a pointer to Thread? Thread (this) does not appear to be passed to pthread_create.
It's great that you're attempting to write a Thread class for educational purposes. However, if you're not, why reinvent the wheel?
pThis is most likely NULL, you should double check that you're passing the correct arguments to pthread_create.
Basically, the problem is as soon as you start your thread, main exits and your local Thread instance goes out of scope. So, because the lifetime of your thread object is controlled by another thread, you've already introduced a race condition.
Also, I'd consider joining a thread immediately after you've created it in Thread::start to be a little odd.
I have a question concerning this code which I want to run on QNX:
class ConcreteThread : public Thread
{
public:
ConcreteThread(int test)
{
testNumber = test;
}
void *start_routine()
{
for(int i = 0; i < 10; i++)
{
sleep(1);
cout << testNumber << endl;
}
}
private:
int testNumber;
};
class Thread
{
public:
Thread(){};
int Create()
{
pthread_t m_id;
return pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this);
}
protected:
virtual void *start_routine() = 0;
private:
static void *start_routine_trampoline(void *p)
{
Thread *pThis = (Thread *)p;
return pThis->start_routine();
}
};
Now, when I run this code without the sleep in *start_routine, it will simply print the number 10 times, before continuing on to the next line of code (sequential instead of parallel). However, when I use a sleep like in the code, it doesn't print any numbers at all and simply goes on to the next line of code. Why doesn't sleep work and how can I make a thread like this work, instead of running sequential?
Note 1: If you only have 1 processor the code can only be done sequentially no matter how many threads you create. Each thread is given a slice of processor time before it is swapped out for the next threads.
Note 2: If the main thread exits pthreads will kill all child threads before they have a chance to execute.
Now to answer you questions:
Without the sleep. The thread once started has enough time in the single slice it was given to execute the loop 10 times completely.
With the sleep: Your worker thread is going to sleep for a full second. So your main thread has time to do a lot of work. If the main thread exits in this time the worker will be killed.
I would make the following changes:
// Remove the Create() method
// Put thread creation in the constructor.
// Make the thread variable part of the object
pthread_t m_id;
Thread()
{
if (pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this) != 0)
{
throw std::runtime_error("Thread was not created");
}
}
// Make sure the destructor waits for the thread to exit.
~Thread()
{
pthread_join(m_id);
}
If you go and look at boost threading library. you will find that all the little mistakes like this have already been taken care of; Thus making threading easier to use.
Also note. That using a static may work but it is non portable. This is because pthread's is a C library and is thus expecting a function pointer with a C ABI. You are just getting lucky for your platform here. You need to define this as a function and declare the ABI by using extern "C"
// This needs to be a standard function with C Interface.
extern "C" void *start_routine_trampoline(void *p)
{
}
Try to make the pthread_t id a class member instead of a function local variable. That way the caller can pthread_join it.
Not doing this is technically a resource leak (unless the thread is specifically not joinable). And joining will avoid the issue that Martin York described.
From man pthread_join:
The joined thread th must be in the joinable state: it must not have
been detached using pthread_detach(3) or the PTHREAD_CREATE_DETACHED
attribute to pthread_create(3).
When a joinable thread terminates, its memory resources (thread
descriptor and stack) are not deallocated until another thread performs
pthread_join on it. Therefore, pthread_join must be called once for
each joinable thread created to avoid memory leaks.
Going off on a tangent here... With respect to Martin York's post:
Also note. That using a static may work but it is non portable. This is because pthread's is a C library and is thus expecting a function pointer with a C ABI. You are just getting lucky for your platform here. You need to define this as a function and declare the ABI by using extern "C" // This needs to be a standard function with C Interface. extern "C" void * start_routine_trampoline(void * p) {...}
I'm not so sure about that...
(1) C++ was designed to be as compatible with C as possible. There are a few differences... But I was under the impression that extern "C" was used mostly to circumvent the name-mangling required to implement C++ function overloading.
(2) It seems like, once you have the function pointer, the calling conventions (what gets pushed on the stack to make the function call) just has to be the same between C & C++. Otherwise, how would function pointers work?
E.g.:
C code:
void bar( int i ) { printf( "bar %d\n", i ); }
C++ code:
class Foo
{
public:
static void foo( int i ) { cout << "foo " << i << endl; }
};
extern "C" { void bar(int); }
int main()
{
void (*p)(int);
p = & Foo::foo;
(*p)(1);
p = & bar;
(*p)(2);
}