3

I am trying to learn multithreading in C++. I am trying to pass elements of a vector as arguments to pthread_create. However, it is not working as expected.

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <vector>
using namespace std;
void *count(void *arg)
{
    int threadId = *((int *)arg);
    cout << "Currently thread with id " << threadId << " is executing " << endl;
    pthread_exit(NULL);
}
int main()
{
    pthread_t thread1;
    vector<int> threadId(2);
    threadId[0] = 99;
    threadId[1] = 100;
    int retVal = pthread_create(&thread1, NULL, count, (void *)&threadId[0]);
    if (retVal)
    {
        cout << "Error in creating thread with Id: " << threadId[0] << endl;
        exit(-1);
    }
    pthread_t thread2;
    retVal = pthread_create(&thread2, NULL, count, (void *)&threadId[1]);
    if (retVal)
    {
        cout << "Error in creating thread with Id: " << threadId[1] << endl;
        exit(-1);
    }
    pthread_exit(NULL);
}

The output which I get is:

Currently thread with id 99 is executing.
Currently thread with id 0 is executing

However, according to me, it should be:

Currently thread with id 99 is executing.
Currently thread with id 100 is executing.

What am I missing here ?

5
  • 3
    Is there any reason you don't use std::thread? It has way better interface. Commented Jun 26, 2022 at 11:26
  • 2
    First of all, don't use pthreads. C++11 onward has std::thread (or better std::async/std::future). Other very useful things to learn about are lambda functions (and their capture behavior), std::shared_ptr (for sharing objects between threads) AND std::mutex (and std::unqiue_lock/std::scoped_lock) to protected your data. About your issue the order in which threads are executed are not down to you, it is the operating thread scheduler that does that. Commented Jun 26, 2022 at 11:26
  • 2
    @HolyBlackCat I find std::async abetter abstraction. The returned future allows for almost painless synchronization with the "calling" thread when needed. In other words std::thread is more a "how" and std::async is more a "what". Commented Jun 26, 2022 at 11:28
  • 2
    You should join the threads before leaving main, otherwise you've got a race condition on the vector (which gets destroyed at the end of main) Commented Jun 26, 2022 at 11:29
  • 2
    @Mat note that the destructor of std::future returned by std::async will do that synchronization. Just make sure the vector is created before the futures and it will be fine. This one of the other reasons I prefer std::async Commented Jun 26, 2022 at 11:36

1 Answer 1

2
int retVal = pthread_create(&thread1, NULL, count, (void *)&threadId[0]);

You have no guarantee, whatsoever, that the new execution thread is now running, right this very instant, without any delay.

All that pthread_create guarantees you is that the thread function, thread1, will begin executing at some point. It might be before pthread_create() itself returns. Or it might be at some point after. It's really a big mystery when the new thread function will start executing, but you can take it to the bank that the new execution thread will begin. Eventually.

The same thing goes for your 2nd execution thread.

So, both execution thread could very well get in gear after your main() returns, and after your vector gets destroyed. There's nothing in the shown code that guarantees that the execution threads will execute before the vector (whose contents get passed into them, in the manner shown) gets destroyed. And this leads to undefined behavior.

You will need to use other thread-related facilities that must be employed in order to synchronize multiple execution threads correctly. Additionally, you're using older POSIX threads. Modern C++ uses std::threads which offer many advantages over their predecessor, is completely type-safe (no ugly casts) and have numerous attributes that prevent common programming errors (however, in this instance std::threads also have no synchronization guarantee, this is usually the case with all typical execution thread implementations).

Sign up to request clarification or add additional context in comments.

1 Comment

It's worth mentioning that the pthreads fix for main() returning before the spawned threads have finished executing would be to call pthread_join() on each of the spawned threads at (or near) the end of main()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.