C++11 std::threads and waiting for threads to finish

折月煮酒 提交于 2019-12-11 00:48:03

问题


I have a vector of Timer Objects. Each Timer Object launches an std::thread that simulates a growing period. I am using a Command pattern.

What is happening is each Timer is getting executed one after another but what I really want is for one to be executed....then once finished, the next one...once finished the next...while not interfering with the main execution of the program

class Timer 
{
    public:

        bool _bTimerStarted;
        bool _bTimerCompleted;

        int _timerDuration;

        virtual ~Timer() { }
        virtual void execute()=0;
        virtual void runTimer()=0;

        inline void setDuration(int _s) { _timerDuration = _s; };
        inline int getDuration() { return _timerDuration; };

        inline bool isTimerComplete() { return _bTimerCompleted; };
};

class GrowingTimer : public Timer
{
    public:
        void execute()
        {
            //std::cout << "Timer execute..." << std::endl;

            _bTimerStarted = false;
            _bTimerCompleted = false;

            //std::thread t1(&GrowingTimer::runTimer, this); //Launch a thread
            //t1.detach();

            runTimer();
        }

        void runTimer()
        {
            //std::cout << "Timer runTimer..." << std::endl;

            _bTimerStarted = true;

            auto start = std::chrono::high_resolution_clock::now();
            std::this_thread::sleep_until(start + std::chrono::seconds(20));

            _bTimerCompleted = true;

            std::cout << "Growing Timer Finished..." << std::endl; 
        }
};

class Timers
{
    std::vector<Timer*> _timers;

    struct ExecuteTimer
    {
        void operator()(Timer* _timer) { _timer->execute(); }
    };

    public:
        void add_timer(Timer& _timer) { _timers.push_back(&_timer); }

        void execute()
        {
            //std::for_each(_timers.begin(), _timers.end(), ExecuteTimer());

            for (int i=0; i < _timers.size(); i++)
            {
                 Timer* _t = _timers.at(i);
                _t->execute();

                //while ( ! _t->isTimerComplete())
                //{

                //}
            }
        }
};

Executing the above like:

Timers _timer;
GrowingTimer _g, g1;

_g.setDuration(BROCCOLI::growTimeSeconds);
_g1.setDuration(BROCCOLI::growTimeSeconds);

_timer.add_timer(_g);
_timer.add_timer(_g1);

start_timers();

}

void start_timers() 
{
    _timer.execute();
}

In Timers::execute I am trying a few different ways to execute the first and not execute the next until I somehow signal it is done.

UPDATE:

I am now doing this to execute everything:

Timers _timer;
GrowingTimer _g, g1;

_g.setDuration(BROCCOLI::growTimeSeconds);
_g1.setDuration(BROCCOLI::growTimeSeconds);

_timer.add_timer(_g);
_timer.add_timer(_g1);

//start_timers();

std::thread t1(&Broccoli::start_timers, this); //Launch a thread
t1.detach();

}

void start_timers() 
{
    _timer.execute();
}

The first time completes (I see the "completed" cout), but crashes at _t->execute(); inside the for loop with an EXEC_BAD_ACCESS. I added a cout to check the size of the vector and it is 2 so both timers are inside. I do see this in the console:

this    Timers *    0xbfffd998
_timers std::__1::vector<Timer *, std::__1::allocator<Timer *> >

if I change the detach() to join() everything completes without the crash, but it blocks execution of my app until those timers finish.


回答1:


Why are you using threads here? Timers::execute() calls execute on a timer, then waits for it to finish, then calls execute on the next, and so forth. Why don't you just call the timer function directly in Timers::execute() rather than spawning a thread and then waiting for it?

Threads allow you to write code that executes concurrently. What you want is serial execution, so threads are the wrong tool.

Update: In the updated code you run start_timers on a background thread, which is good. However, by detaching that thread you leave the thread running past the end of the scope. This means that the timer objects _g and _g1 and even the Timers object _timers are potentially destroyed before the thread has completed. Given the time-consuming nature of the timers thread, and the fact that you used detach rather than join in order to avoid your code blocking, this is certainly the cause of your problem.

If you run code on a thread then you need to ensure that all objects accessed by that thread have a long-enough lifetime that they are still valid when the thread accesses them. For detached threads this is especially hard to achieve, so detached threads are not recommended.

One option is to create an object containing _timers, _g and _g1 along side the thread t1, and have its destructor join with the thread. All you need to do then is to ensure that the object lives until the point that it is safe to wait for the timers to complete.




回答2:


If you don't want to interfere with the execution of the program, you could do something like @Joel said but also adding a thread in the Timers class which would execute the threads in the vector.




回答3:


You could include a unique_ptr to the thread in GrowingTimer instead of creating it as a local object in execute and calling detach. You can still create the thread in execute, but you would do it with a unique_ptr::reset call.

Then use join instead of isTimerComplete (add a join function to the Timer base class). The isTimerComplete polling mechanism will be extremely inefficient because it will basically use up that thread's entire time slice continually polling, whereas join will block until the other thread is complete.

An example of join:

#include <iostream>
#include <chrono>
#include <thread>

using namespace std;

void threadMain()
{
    this_thread::sleep_for(chrono::seconds(5));

    cout << "Done sleeping\n";
}

int main()
{
    thread t(threadMain);

    for (int i = 0; i < 10; ++i)
    {
        cout << i << "\n";
    }

    t.join();

    cout << "Press Enter to exit\n";

    cin.get();
    return 0;
}

Note how the main thread keeps running while the other thread does its thing. Note that Anthony's answer is right in that it doesn't really seem like you need more than one background thread that just executes tasks sequentially rather than starting a thread and waiting for it to finish before starting a new one.



来源:https://stackoverflow.com/questions/18094510/c11-stdthreads-and-waiting-for-threads-to-finish

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!