Подтвердить что ты не робот

Отложенный запуск потока в С++ 11

Я попадаю в потоки С++ 11 и столкнулся с проблемой.

Я хочу объявить переменную потока как глобальную и начать ее позже.

Однако все примеры, которые я видел, похоже, запускают поток сразу, например,

thread t(doSomething);

Я хочу

thread t;

и запустите поток позже.

То, что я пробовал,

if(!isThreadRunning)
{
    thread t(readTable);
}

но теперь t - область блока. Поэтому я хочу объявить t, а затем начать поток позже, чтобы t был доступен для других функций.

Спасибо за любую помощь.

4b9b3361

Ответ 1

std::thread конструктор по умолчанию создает экземпляр std::thread без запуска или представления любого фактического потока.

std::thread t;

Оператор присваивания перемещает состояние объекта потока и устанавливает назначенный объект потока в его инициализированное по умолчанию состояние:

t = std::thread(/* new thread code goes here */);

Вначале создается объект временного потока, представляющий новый поток, передает новое представление потока в существующий объект потока, который имеет состояние по умолчанию, и устанавливает состояние состояния временного потока в состояние по умолчанию, которое не представляет ни один поток. Затем временный объект потока уничтожается, ничего не делая.

Вот пример:

#include <iostream>
#include <thread>

void thread_func(const int i) {
    std::cout << "hello from thread: " << i << std::endl;
}

int main() {
    std::thread t;
    std::cout << "t exists" << std::endl;

    t = std::thread{ thread_func, 7 };
    t.join();

    std::cout << "done!" << std::endl;
}

Ответ 2

Я бы дал потоку переменную условия и логическое имя startRunning (изначально было установлено значение false). Эффективно вы должны начать поток сразу после создания, но первое, что он сделал бы, это приостановить себя (используя условие_вариабельный), а затем начать обработку своей фактической задачи, когда сигнализация condition_variable извне (и startRunning установлено значение true).

EDIT: PSEUDO CODE:

// in your worker thread
{
    lock_guard l( theMutex );

    while ( ! startRunning )
    {
        cond_var.wait( l );
    }
}

// now start processing task


// in your main thread (after creating the worker thread)
{
    lock_guard l( theMutex );
    startRunning = true;
    cond_var.signal_one();
}

EDIT # 2: В приведенном выше коде переменные theMutex, startRunning и cond_var должны быть доступны обоими потоками. Достигаете ли вы этого, делая их глобальными или инкапсулируя их в экземпляр struct/class, зависит от вас.

Ответ 3

Нет "стандартного" создания потока "приостановлено", которое я предполагаю, это то, что вы хотели сделать с библиотекой потоков С++. Поскольку он не поддерживается на каждой платформе, имеющей потоки, ее нет в С++ API.

  • Возможно, вам захочется создать класс со всеми необходимыми данными, но не на самом деле запустить функцию потока. Это не то же самое, что создавать поток, но может быть тем, что вы хотите. Если это так, создайте это, а затем свяжите объект и его функцию operator() или start() или что-то в этом потоке.

  • Вам может понадобиться идентификатор потока для вашего потока. Это означает, что вам действительно нужно запустить функцию потока. Однако он может начинаться с ожидания переменной состояния. Затем вы передаете или передаете эту переменную условия позже, когда хотите продолжить ее выполнение. Конечно, вы можете проверить функцию, после того, как она возобновится, если вы, возможно, решили закрыть ее и не запустить ее в конце (в этом случае она просто вернется сразу).

  • Вам может понадобиться объект std::thread без функции. Вы можете сделать это и прикрепить его к функции позже run, которая будет работать в новом потоке.

Ответ 4

Вы можете использовать singleton pattern. Или я скорее скажу antipattern.

Внутри одного сингла вы должны иметь объект std::thread, инкапсулированный. При первом доступе к singleton ваш поток будет создан и запущен.

Ответ 5

Как говорит в своем ответе antred, вы можете использовать условную переменную, чтобы заставить поток ждать в начале своей процедуры.

Скотт Мейерс в своей книге "Эффективное современное C++" (в "Пункте 39: Рассмотрите void фьючерсы для одноразовой передачи событий") предлагает использовать void -future вместо сущностей более низкого уровня (логический флаг, условная переменная и мьютекс)). Так что проблема может быть решена так:

auto thread_starter = std::promise<void>;
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)

Скотт Мейерс также предупреждает об исключениях во втором (отмечен тем, что you can do something, thread is like "paused" here комментарий). Если thread_starter.set_value() никогда не вызывается по некоторым причинам (например, из-за исключений, генерируемых во втором ), поток будет ждать вечно, и любая попытка присоединиться к нему приведет к взаимоблокировке.

Поскольку оба способа (на основе condvar и на основе будущего) содержат скрытую небезопасность, а первый способ (на основе condvar) требует некоторого стандартного кода, я предлагаю написать класс-оболочку вокруг std::thread. Его интерфейс должен быть похож на интерфейс std::thread (за исключением того, что его экземпляры должны назначаться из других экземпляров того же класса, а не из std::thread), но содержать дополнительный метод void start().

Основанная на будущем нить-обертка

class initially_suspended_thread {
    std::promise<bool> starter;
    std::thread impl;
public:
    template<class F, class ...Args>
    explicit initially_suspended_thread(F &&f, Args &&...args):
        starter(),
        impl([
            starter_future = starter.get_future(),
            routine = std::bind(std::forward<F>(f), std::forward<Args>(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 нить-обертка

class initially_suspended_thread {
    std::mutex state_mutex;
    enum {INITIAL, STARTED, ABORTED} state;
    std::condition_variable state_condvar;
    std::thread impl;
public:
    template<class F, class ...Args>
    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>(f), std::forward<Args>(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
};

Ответ 6

первый объявленный в классе m_grabber ничего не запускает. Мы присваиваем объект класса-члена новый объект с лямбда-функцией в методе launch_grabber, а поток с лямбда-выражениями выполняется в контексте класса source.

class  source {
...
 std::thread m_grabber;
 bool m_active;
...
}


bool source::launch_grabber() {
    // start grabber
    m_grabber = std::thread{ 
        [&] () {
            m_active = true;
            while (true)
            {
                if(!m_active)
                    break;
                // TODO: something in new thread

            }
        }
    };

    m_grabber.detach();
    return true;
}