I am practicing using pointers and stumbled upon something I don’t understand. The program does this:
- Create a vector
- Pass vector’s address to a function
- That function has a for-loop
- In that for-loop a user is asked for a movie name
- Upon receiving the movie name, a new movie object is made (from struct)
- For each movie a new boost thread is created, passing the title the user made up and a pointer for both the new movie object and the vector.
- In the boost thread, the movie object’s “title” variable is assigned the title the user made up and the movie is then added to the vector
- When all threads are done, a for-loop inside the “main” function shows all movie titles stored in the vector.
The problem occurs when I swap these two
//Get info about new movie from user
string movieTitle;
int movieYear; //Not used at the moment
cout << "Enter title for movie " << (i+1) << endl;
getline(cin,movieTitle);
and
//Create new movie
movies_t amovie;
movies_t * pmovie;
pmovie = &amovie;
When I put the “user input part” above the “create a new movie” part, like it is right now, I get this:

But when I swap them:

I don’t understand why because they aren’t affecting each other at all.
The data is shown like this:
for(i=0;i<movieVector.size();i++)
{
cout << movieVector[i].title << endl;
}
These are the relevant functions (main and newThreads)
void newThreads(vector<movies_t> *movieVectorPointer)
{
boost::thread_group group; //Start thread group
int i;
for(i=0; i<2; i++) //Make X amount of threads (2 in this case)
{
//Get info about new movie from user
string movieTitle;
int movieYear; //Not used at the moment
cout << "Enter title for movie " << (i+1) << endl;
getline(cin,movieTitle);
//Create new movie
movies_t amovie;
movies_t * pmovie;
pmovie = &amovie;
//Let user know we are now starting on this thread
cout << "Doing thread " << i << endl;
//Start new thread
newThreadStruct startNewThread(movieTitle,movieYear,pmovie,movieVectorPointer);
group.create_thread(startNewThread);
}
group.join_all(); //Wait for all threads in group to finish
}
int main()
{
cout << "Hello world!" << endl; //I am born.
vector<movies_t> movieVector; //Create vector to store movies in
newThreads(&movieVector); //Start making new threads. Pass movieVector's address so it can be used within threads.
/* The below code will not be executed until all threads are done */
cout << "Amount of movies " << movieVector.size() << endl; //Let user know how many movies we made
//Show all movies we made
int i;
for(i=0;i<movieVector.size();i++)
{
cout << movieVector[i].title << endl;
}
cout << "Bye world!" << endl; //This life has passed.
return 0;
}
And here is the full code, in case it matters:
#include <iostream>
#include <boost/thread.hpp>
using namespace std;
//A movie will hold a title and a year. Only title is used in this code.
struct movies_t {
string title;
int year;
};
//This function is where the data is added to our new movie,a fter which our finished movie will be added to the vector. It is called from within the "newThreadStruct" operator.
int doMovieWork(string movieTitle,int movieYear,movies_t *moviePointer,vector<movies_t> *movieVector)
{
moviePointer->title = movieTitle; //Add title to new movie
movieVector->push_back(*moviePointer); //Add movie to vector
return 0;
};
//This struct will be used to create new Boost threads. It accepts various arguments.
struct newThreadStruct
{
newThreadStruct(string movieTitle,int movieYear,movies_t *moviePointer,vector<movies_t> *movieVector) : movieTitle(movieTitle),movieYear(movieYear),moviePointer(moviePointer),movieVector(movieVector) { }
void operator()()
{
doMovieWork(movieTitle,movieYear,moviePointer,movieVector);
}
string movieTitle;
int movieYear;
movies_t *moviePointer;
vector<movies_t> *movieVector;
};
void newThreads(vector<movies_t> *movieVectorPointer)
{
boost::thread_group group; //Start thread group
int i;
for(i=0; i<2; i++) //Make X amount of threads (2 in this case)
{
//Get info about new movie from user
string movieTitle;
int movieYear; //Not used at the moment
cout << "Enter title for movie " << (i+1) << endl;
getline(cin,movieTitle);
//Create new movie
movies_t amovie;
movies_t * pmovie;
pmovie = &amovie;
//Let user know we are now starting on this thread
cout << "Doing thread " << i << endl;
//Start new thread
newThreadStruct startNewThread(movieTitle,movieYear,pmovie,movieVectorPointer);
group.create_thread(startNewThread);
}
group.join_all(); //Wait for all threads in group to finish
}
int main()
{
cout << "Hello world!" << endl; //I am born.
vector<movies_t> movieVector; //Create vector to store movies in
newThreads(&movieVector); //Start making new threads. Pass movieVector's address so it can be used within threads.
/* The below code will not be executed until all threads are done */
cout << "Amount of movies " << movieVector.size() << endl; //Let user know how many movies we made
//Show all movies we made
int i;
for(i=0;i<movieVector.size();i++)
{
cout << movieVector[i].title << endl;
}
cout << "Bye world!" << endl; //This life has passed.
return 0;
}
The order in which you get user input and initialize the
movies_tobject is a red herring. The actual problem is here:You are passing the address of a local variable (
amovie) to a thread. You have no direct control over when this thread starts, when it tries to access the pointer you’ve passed it, or when it exits. But you don’t wait in the main thread loop for the worker thread to use the local. As soon as the loop loops, the variable you’ve passes falls out of scope. When the worker thread tries to use it, you invoke undefined behavior. This is very bad.Probably the simplest (and most naive) way to fix this, is to dynamically create the
amovieobject:…and then when you’re done using it,
deleteit somewhere. From my initial inspection of your code, where todeleteit was not immediately obvious — but it could be at the end ofmain.This naive approach opens a huge can of worms with respect to pointer ownership, memory management and potentially deadlocking and race conditions. You have now entered the realm of multithreadded programming — one of the most difficult things to do correctly there is in C++ programming. The naieve approach above will “work” (as in keep your code from crashing), albiet not without flaws — but if you’re going down the road of multithreadded programming, it’s time to start studying how to do it right. That is way beyond the scope of my little post here.