Home > OS >  How to pass and start multiple threads within a function?
How to pass and start multiple threads within a function?

Time:11-20

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.

CodePudding user response:

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";
}

CodePudding user response:

You can pack these functions into a tuple, and then pack the parameters corresponding to each function into a tuple, and then pass them into startThread() together, then expand the function through std::apply, and then expand the corresponding parameters through std::apply and pass them into the function. Something like this:

template<class FunTuple, class... ArgsTuple>
int startThread(FunTuple fun_tuple, ArgsTuple... args_tuple) {
  std::vector<std::thread> threads;
  std::apply([&](auto... fun) {
    (threads.emplace_back(
      [&] { std::apply([&](auto... args) { fun(args...); }, args_tuple); }
    ), ...);
  }, fun_tuple);
  std::cout << "synchronizing all threads...\n";
  for (auto& th : threads ) th.join();
  return 0;
}

Then you can invoke startThread() like this:

startThread(std::tuple(test1, test2), std::tuple(2, 65.2), std::tuple('A'));

Demo.

  • Related