Search code examples
c++multithreadingnestedstdthread

Error nested std::thread inside a thread throws an error


I'm trying to get this simple test to create a nested thread inside another thread, but it keeps failing. Tried removing this nested function and the code works fine on its own.

Code example/test:

#include <thread>
#include <iostream>
#include <vector>

void simple() {  std::cout << "\nNested Thread";  }

void function(int number, int* sum, std::vector<std::thread>& thread_group) {
    *sum += number;

    // Starting this nested function simple() in a thread causes errors.
    // The problem occurs even when there's only a single iteration of the loop.
    // Without it the code works fine, even with 10000 iterations.
    std::thread ct(simple);
    thread_group.push_back(std::move(ct));
}

int main(){
    // Container for storing threads
    std::vector<std::thread> thread_group;
    int sum = 0;
    for (int n = 1; n <= 10; n++) {
        std::thread t(function, n, &sum, std::ref(r), std::ref(thread_group));
        // Add the newly created thread to the container
        thread_group.push_back(std::move(t));
    }
    // Wait for all threads to finish
    join_all(thread_group);
    return 0;
}

That being said, I need to find a way call an independent function that could run without blocking the execution of the rest of the code. Threading or some other way of multiprocessing is very important in my program because each function is recursive and keeps calling multiple copies of itself with different parameters untill a condition is satisfied.

I don't know whether it's even possible in the std::thread library. Tried using Poco and boost, but was faced with other problems, so decided to stick to std::thread instead.

I'm using Visual Studio 2022 on Windows 10.


Solution

  • You are reading and writing to sum and thread_group from multiple threads at the same time. A simple solution is you make sure that only one thread at a time has access to them by using a std::mutex and locking it while accessing those variables. You also need to make sure that all threads are finished working with thread_group before it gets destroyed. I added a counter and a std::condition_variable to take care of that.

    Example (using C++20 std::jthreads instead to not have to call the non-existing join_all function):

    #include <condition_variable>
    #include <iostream>
    #include <list>     // using list instead of vector to avoid realloc
    #include <mutex>
    #include <thread>
    #include <vector>
    
    using container = std::list<std::jthread>;
    
    void simple() {  std::cout << "Nested Thread\n";  }
    
    void function(int& counter, std::mutex& mtx,
                  std::condition_variable& cv, container& thread_group)
    {
        std::lock_guard lock(mtx);
        thread_group.emplace_back(simple);
        ++counter;       // so that main thread can check that all is done
        cv.notify_one(); // signal main that the work is done
    }
    
    int main(){
        // Container for storing threads
        container thread_group;
        std::mutex mtx;
        std::condition_variable cv;
    
        int starting_threads = 10;
        int counter = 0;
        for (int n = 1; n <= starting_threads; n++) {
            std::lock_guard lock(mtx); // lock while adding thread
            // Add thread to the container
            thread_group.emplace_back(function, std::ref(counter), std::ref(mtx),
                                      std::ref(cv), std::ref(thread_group));
        }   
        
        std::unique_lock lock(mtx);
        // wait until all threads are done with thread_group
        // before letting thread_group go out of scope and get
        // destroyed
        while(counter < starting_threads) cv.wait(lock);
    
        std::cout << thread_group.size() << '\n';
    
        // wait for all threads to finish (jthread's auto-join)
    }
    

    Possible output:

    Nested Thread
    Nested Thread
    Nested Thread
    Nested Thread
    Nested Thread
    Nested Thread
    Nested Thread
    Nested Thread
    Nested Thread
    20
    Nested Thread