Delayed start of a thread in C++ 11

前端 未结 6 1955
南方客
南方客 2020-12-01 09:05

I\'m getting into C++11 threads and have run into a problem.

I want to declare a thread variable as global and start it later.

However all the examples I\'ve

6条回答
  •  日久生厌
    2020-12-01 09:41

    As antred says in his answer, you can use a condition variable to make the thread to wait in the beginning of its routine.

    Scott Meyers in his book “Effective Modern C++” (in the “Item 39: Consider void futures for one-shot event communication”) proposes to use void-future instead of lower level entities (boolean flag, conditional variable and mutex). So the problem can be solved like this:

    auto thread_starter = std::promise;
    auto thread = std::thread([starter_future = thread_starter.get_future()]() mutable {
        starter_future.wait(); //wait before starting actual work
        …; //do actual work
    });
    …; //you can do something, thread is like “paused” here
    thread_starter.set_value(); //“start” the thread (break its initial waiting)
    

    Scott Meyers also warns about exceptions in the second (marked by the you can do something, thread is like “paused” here comment). If thread_starter.set_value() is never called for some reasons (for example, due to exception throws in the second ), the thread will wait forever, and any attempt to join it would result in deadlock.

    As both ways (condvar-based and future-based) contain hidden unsafety, and the first way (condvar-based) needs some boilerplate code, I propose to write a wrapper class around std::thread. Its interface should be similar to the one of std::thread (except that its instances should be assignable from other instances of the same class, not from std::thread), but contain additional void start() method.

    Future-based thread-wrapper

    class initially_suspended_thread {
        std::promise starter;
        std::thread impl;
    public:
        template
        explicit initially_suspended_thread(F &&f, Args &&...args):
            starter(),
            impl([
                starter_future = starter.get_future(),
                routine = std::bind(std::forward(f), std::forward(args)...)
            ]() mutable {if (starter_future.get()) routine();})
        {}
        void start() {starter.set_value(true);}
        ~initially_suspended_thread() {
            try {starter.set_value(false);}
            catch (const std::future_error &exc) {
                if (exc.code() != std::future_errc::promise_already_satisfied) throw;
                return; //already “started”, no need to do anything
            }
            impl.join(); //auto-join not-yet-“started” threads
        }
        …; //other methods, trivial
    };
    

    Condvar-based thread-wrapper

    class initially_suspended_thread {
        std::mutex state_mutex;
        enum {INITIAL, STARTED, ABORTED} state;
        std::condition_variable state_condvar;
        std::thread impl;
    public:
        template
        explicit initially_suspended_thread(F &&f, Args &&...args):
            state_mutex(), state(INITIAL), state_condvar(),
            impl([
                &state_mutex = state_mutex, &state = state, &state_condvar = state_condvar,
                routine = std::bind(std::forward(f), std::forward(args)...)
            ]() {
                {
                    std::unique_lock state_mutex_lock(state_mutex);
                    state_condvar.wait(
                        state_mutex_lock,
                        [&state]() {return state != INITIAL;}
                    );
                }
                if (state == STARTED) routine();
            })
        {}
        void start() {
            {
                std::lock_guard state_mutex_lock(state_mutex);
                state = STARTED;
            }
            state_condvar.notify_one();
        }
        ~initially_suspended_thread() {
            {
                std::lock_guard state_mutex_lock(state_mutex);
                if (state == STARTED) return; //already “started”, no need to do anything
                state = ABORTED;
            }
            impl.join(); //auto-join not-yet-“started” threads
        }
        …; //other methods, trivial
    };
    

提交回复
热议问题