I have a piece of code that simulates the provider/consumer scenario where each provider and consumer is a thread. I've paired each consumer with a provider and the consumer will wait until its provider has finished (by calling join()
on the provider thread) before executing. The code is as follows:
std::vector<std::thread> threads;
const uint32_t num_pairs = 3;
auto provider = [&](uint32_t idx) {
/* produce resources and put them in a global container */
};
auto consumer = [&](uint32_t idx) {
/* consumer i is paired with provider (i-num_pairs) */
uint32_t provider_idx = idx - num_pairs;
threads[provider_idx].join();
assert(threads[provider_idx].joinable() == false);
/* access the resources */
};
for (uint32_t i = 0; i < 2 * num_pairs; i ) {
if (i < num_pairs) {
/* 0, 1, 2 are providers */
threads.emplace_back(provider, i);
} else {
/* 3, 4, 5 are consumers */
threads.emplace_back(consumer, i);
}
}
/* join the consumer threads later */
Most of the time it works fine but sometimes the assertion in consumer
fails and the provider thread is still joinable after it has been joined. Is the implementation incorrect or is there something I am not aware of happening? Please help and thanks in advance!
CodePudding user response:
The vector threads
is growing as you push be threads into it, there's a good chance that some of the threads will start executing before you've finished adding all the threads. When the vectors capacity increases all iterators and references to it become invalid. This means that in threads[provider_idx]
the returned reference could be invalidated between the return of the []
operator and the execution of the thread method. As this is undefined behaviour your observed misbehaviour of join
and joinable
isn't unexpected.
Reserving the size of the vector before creating the threads should fix the problem:
threads.reserve(2 * num_pairs);