简体   繁体   中英

Running C++ threads from different scope using join()

Let's assume situation that we have two functions:

void foo1 () { 
    while (true) { 
        std::cout << "foo1" << std::endl;
        std::this_thread::sleep_for (std::chrono::milliseconds {100});
    }
}

void foo2 () { 
    while (true) { 
        std::cout << "foo2" << std::endl;
        std::this_thread::sleep_for (std::chrono::milliseconds {100});
    }
}

and I want to start them in different threads but whether they are started is dependent on some condition, so for example execution of them is just like below:

bool c1 {true};
bool c2 {true};

if (c1) { 
    std::thread th1 {&foo1};
    th1.join ();
}

if (c2) { 
    std::thread th2 {&foo2};
    th2.join ();
}

I know that here in this situation only foo1() will be invoked and foo2() never.

My first thought was to use unique_ptr like this:

bool c1 {false};
bool c2 {false};

std::unique_ptr<std::thread> pth1 {};
std::unique_ptr<std::thread> pth2 {};

if (c1) { 
    pth1 = std::unique_ptr<std::thread> {new std::thread {&foo1}};
}

if (c2) { 
    pth2 = std::unique_ptr<std::thread> {new std::thread {&foo2}};
}  

if (pth1) { 
    pth1->join ();
}

if (pth2) { 
    pth2->join ();
}

My second thought was to change design a little bit to run threads always, but if condition is false, exit function, so take a look at the code below:

void foo1 (bool c) { 
    if (c) {
        while (true) { 
            std::cout << "foo1" << std::endl;
            std::this_thread::sleep_for (std::chrono::milliseconds {100});
        }
    }
}

void foo2 (bool c) { 
    if (c) {
        while (true) { 
            std::cout << "foo2" << std::endl;
            std::this_thread::sleep_for (std::chrono::milliseconds {100});
        }
    }
}

bool c1 {true};
bool c2 {true};

std::thread th1 {&foo1, c1};
std::thread th2 {&foo2, c2};

th1.join ();
th2.join ();

I know that asking which one is better is not always good question, but could you suggest me good (and maybe better than those presented) solution to handle situation when at least two threads are starting from different scopes and all of them have to be joined?

A std::thread object doesn't have to represent a thread. I think the simplest is:

    std::thread th1, th2;
    if (c1)
        th1 = std::thread{&foo1};
    if (c2)
        th2 = std::thread{&foo2};

    if (th1.joinable())
        th1.join();
    if (th2.joinable())
        th2.join();

Or even:

std::thread maybe_start( void(*f)(), bool c)
{
    if (c)
        return std::thread{f};
    else
        return {}
}
void maybe_wait(std::thread& thr)
{
    if (thr.joinable())
        thr.join();
}

....
    std::thread thr1 = maybe_start(&foo1, c1);
    std::thread thr2 = maybe_start(&foo2, c2);

    maybe_wait(thr1);
    maybe_wait(thr2);

You need some thing like thread pool. I wrote this example to demonstrate idea:

#ifndef DUMMYTHREADPOOL_H_
#define DUMMYTHREADPOOL_H_

#include <thread>

class dummy_thread_pool {
private:
    std::vector<std::thread> tp;

    dummy_thread_pool(const dummy_thread_pool&){}

public:
    dummy_thread_pool(){}

    dummy_thread_pool(size_t n)
    {
        tp.reserve(n);
    }

    ~dummy_thread_pool()
    {
        tp.clear();
    }

    void do_join(std::thread& t)
    {
        t.join();
    }

    void join_all() {
        std::for_each(tp.begin(), tp.end(), std::bind(&dummy_thread_pool::do_join, this, std::placeholders::_1));
    }

    void wait_for_all()
    {   
        std::thread t(&dummy_thread_pool::join_all, this);

        t.join();
    }

    void add_thread(std::thread t)
    {
        tp.push_back(std::move(t));
    }
};



#endif /* DUMMYTHREADPOOL_H_ */

Your code may looks like this:

bool c1 {true};
bool c2 {true};
dummy_thread_pool dtp;

if (c1) { 
    std::thread th1 {&foo1};
    dtp.add_thread(std::move(th1));
}

if (c2) { 
    std::thread th2 {&foo2};
    dtp.add_thread(std::move(th2));
}

dtp.wait_for_all();

Also in your case may be useful std::future and std::async instead of std::thread .

Also you can use boost::thread_group :

boost::thread_group tg;
tg.create_thread(/*your thread*/);
tg.join_all();

Your second solution is quite good. But we must do not forget to call join method. For simplify you can use RAII this way:

class thread_guard
{
    std::thread thread_;

public:
    explicit thread_guard( std::thread thread )
        thread_{ std::move( thread ) }
    {
        if ( !thread.joinable( ) ) {
            throw std::logic_error( "No joinable thread!" );
        }
    }

    ~thread_guard( ) {
        thread_.join( );
    }

    thread_guard( const thread_guard& ) = delete;
    thread_guard & operator=( const thread_guard& ) = delete;
};

void f( ) {
    thread_guard thread1( std::thread(&foo1, c1) );
}

Also you can use thread poll with with thread_guard , but in this situation with a small number of threads it's more complicated

Or it's possible to do this very simple way:

std::vector<std::thread> threads;
//add threads in vector
std::for_each(threads.begin(),threads.end(),
    std::mem_fn(&std::thread::join));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM