Question

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?

Was it helpful?

Solution

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)
{
}

OTHER TIPS

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);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top