Reputation: 99
Multithreading program in c++, the problem is: i have a vector of threads, where i push_back every thread i create, and i want to make a kind of "steal-work scheduling", in such a way that when a thread is joined, so i assume his job is terminated, i delete it from the vector of threads and i push_back a new thread assining him another task. The problem is that when i try to erase threads after their join, i got "terminate()" so i assume that i'm erasing threads that are still working, even if my goal was to erase only already joined threads. Where am i wrong?
vector<thread> tids;
const int nw = atoi(argv[1]); //number of worker
//Just erase the thread that has been joined
void erasePosition(thread &t, vector<thread> &threads){
for(int i=0;i<threads.size();i++){
if(t.get_id()==threads[i].get_id()) {
threads.erase(threads.begin()+i);
}
}
}
//Assign job to threads
for (int i = 0; i < nw; i++) {
tids.push_back(thread(solveSubTree, ref(works[i]), ref(nw)));
}
//
for (thread &t : tids) {
t.join();
erasePosition(t,tids);
cout << " tids.size() = " << tids.size() << endl;
}
I see that the program execute correctly until i erase the middle thread, for example if i execute the program with 4 threads, it call "terminate()" when i try to erase the 2nd thread, while if i use 8 threads, it terminate when i try to erase the 4th thread, maybe it will be useful to you.
The exact error is this: terminate called after throwing an instance of 'std::system_error' what(): Invalid argument
Upvotes: 3
Views: 446
Reputation: 118445
for (thread &t : tids) {
Range iteration is implemented using iterators from the underlying container. A simple Google search gives you the equivalent code, which obtains the beginning and the ending iterator from the container, and then iterates over this sequence.
erasePosition(t,tids);
At this point, the internal iterator is referencing the value in this vector that gets passed to erasePosition()
.
erasePosition
() effectively erase
()s this value in the vector.
However, erase()
on a value in a vector "invalidates iterators and references at or after the point of the erase, including the end() iterator". This will, of course, include the iterator to the t
value. The range iteration, from this point on, becomes undefined behavior.
The capsule summary: this has nothing to do with threads. No matter what's in your vector, you cannot do range iteration over a vector, erase the value in the vector, and then continue your range iteration. You will need to implement your task in some other way. This would apply to any other container which invalidates iterators to the value that gets removed from the container (and your homework assignment would be to figure out if there is even any kind of a container that actually allows it).
This may or may not be the only reason for your crash. There may be other reasons too, but because you did not provide a minimal reproducible example it's not possible to ascertain that.
Upvotes: 1