简体   繁体   English

C ++ 11 std :: threads和等待线程完成

[英]C++11 std::threads and waiting for threads to finish

I have a vector of Timer Objects. 我有一个计时器对象向量。 Each Timer Object launches an std::thread that simulates a growing period. 每个Timer对象都会启动一个std :: thread,以模拟一个增长期。 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 发生的事情是每个Timer都被一个接一个地执行,但是我真正想要的是要执行一个...。然后一旦完成,下一个...一旦完成下一个...而不会干扰主程序程序执行

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. 在Timers :: execute中,我尝试了几种不同的方法来执行第一个,而不执行下一个,直到我以某种方式发出信号。

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(); 第一次完成(我看到“完成”的提示),但是在_t->execute();崩溃_t->execute(); inside the for loop with an EXEC_BAD_ACCESS. 在带有EXEC_BAD_ACCESS的for loop I added a cout to check the size of the vector and it is 2 so both timers are inside. 我添加了一个cout来检查向量的大小,它是2,所以两个计时器都在里面。 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. 如果我将detach()更改为join()所有操作都会完成而不会崩溃,但是它将阻止我的应用程序执行,直到这些计时器完成。

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. Timers::execute()调用在一个计时器上execute ,然后等待其结束,然后在下一个execute ,依此类推。 Why don't you just call the timer function directly in Timers::execute() rather than spawning a thread and then waiting for it? 为什么不直接在Timers::execute()直接调用计时器函数,而不是生成线程然后等待呢?

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. 更新:在更新的代码中,您可以在后台线程上运行start_timers ,这很好。 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. 这意味着在线程完成之前,可能会销毁计时器对象_g_g1甚至Timers对象_timers 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. 由于计时器线程的耗时性质,以及您所使用的事实detach ,而不是join ,以避免你的代码阻塞,这肯定是你的问题的原因。

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. 一种选择是沿着线程t1创建一个包含_timers_g_g1的对象,并使它的析构函数与该线程连接。 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. 然后,您需要做的就是确保对象存在,直到可以安全地等待计时器完成为止。

You could include a unique_ptr to the thread in GrowingTimer instead of creating it as a local object in execute and calling detach . 您可以在GrowingTimer中将thread包含unique_ptr ,而不是在execute和调用detach中将其创建为本地对象。 You can still create the thread in execute , but you would do it with a unique_ptr::reset call. 您仍然可以在execute创建线程,但是可以通过unique_ptr::reset调用来实现。

Then use join instead of isTimerComplete (add a join function to the Timer base class). 然后,使用join代替isTimerComplete (将join函数添加到Timer基类)。 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. isTimerComplete轮询机制效率极低,因为它基本上会连续用完该线程的整个时间片,而join会阻塞直到另一个线程完成为止。

An example of join : 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. 请注意,Anthony的答案是正确的,因为您似乎并不需要多个后台线程来按顺序执行任务,而不是启动一个线程并等待其完成之后再启动新线程。

如果您不想干扰程序的执行,可以执行类似@Joel的操作,还可以在Timers类中添加一个线程来执行向量中的线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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