Search code examples
c++multithreadingtemplatesc++20stdthread

How to pass and start multiple threads within a function?


I want to pass in an arbitrary number of functions together with their arguments to a function called startThread so that it can run them concurrently.

My code is below but obviously, it has syntactic errors:


#include <iostream>
#include <thread>
#include <chrono>
#include <vector>
#include <exception>


int test1( int i, double d )
{
    // do something...
    using namespace std::chrono_literals;
    std::this_thread::sleep_for( 3000ms );

    return 0;
}

int test2( char c )
{
    // do something...
    using namespace std::chrono_literals;
    std::this_thread::sleep_for( 2000ms );

    return 0;
}

template< class Fn, class... Args >             // how should I write the template and startThread params to
int startThread( Fn&&... fns, Args&&... args )  // get arbitrary functions as threads and their own arguments?
{
    std::vector< std::thread > threads;

    threads.push_back( std::thread( test1, 2, 65.2 ) ); // how to automate the task of starting the
    threads.push_back( std::thread( test2, 'A' ) );     // threads instead of writing them one by one?

    std::cout << "synchronizing all threads...\n";
    for ( auto& th : threads ) th.join();

    return 0;
}

int main( )
{
    int successIndicator { };

    try
    {
        successIndicator = startThread( test1( 2, 65.2 ), test2( 'A' ) ); // what should be passed to startThread?
    }                                                                     // How to pass the arguments?
    catch ( const std::exception& e )
    {
        successIndicator = -1;
        std::cerr << e.what( ) << '\n';
    }

    return successIndicator;
}

Thanks in advance.


Solution

  • This is how I would do it, using a recursive template function. And std::async instead of std::thread

    #include <future>
    #include <chrono>
    #include <thread>
    #include <iostream>
    
    void test1(int /*i*/, double /*d*/)
    {
        std::cout << "Test1 start\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
        std::cout << "Test1 done\n";
    }
    
    void test2(bool)
    {
        std::cout << "Test2 start\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        std::cout << "Test2 done\n";
    }
    
    //-----------------------------------------------------------------------------
    // Recursive template function that will start all passed functions
    // and then waits for them to be finished one by one.
    // this will still be limited by the slowest function 
    // so no need to store them in a collection or something
    
    template<typename Fn, typename... Fns>
    void run_parallel(Fn fn, Fns&&... fns)
    {
        // I prefer using std::async instead of std::thread
        // it has a better abstraction and futures
        // allow passing of exceptions back to the calling thread.
    
        auto future = std::async(std::launch::async, fn);
        
        // are there any more functions to start then do so
        if constexpr (sizeof...(fns) > 0)
        {
            run_parallel(std::forward<Fns>(fns)...);
        }
    
        future.get();
    }
    
    //-----------------------------------------------------------------------------
    
    int main()
    {
        std::cout << "main start\n";
    
        // start all functions by creating lambdas for them
        run_parallel(
            []() { test1(1, 1.10); },
            []() { test2(true); }
        );
    
        std::cout << "main done\n";
    }