2016-11-11 2 views
5

Это отдельный вопрос, но связанный с предыдущим вопросом, я спросил hereПодхода с использованием зОго :: атомными по сравнению с станд :: condition_variable WRT паузы и возобновлять зЬй :: нитки в C++

Я использую std::thread в моем C++ код для постоянного опроса для некоторых данных & добавьте его в буфер. Я использую C++ lambda, чтобы начать нить так:

StartMyThread() { 

    thread_running = true; 
    the_thread = std::thread { [this] { 
     while(thread_running) { 
      GetData(); 
     } 
    }}; 
} 

thread_running является atomic<bool> объявленными в заголовке класса. Вот моя GetData функция:

GetData() { 
    //Some heavy logic 
} 

Далее я также StopMyThread функцию, где я поставил thread_running ложь так, что он выходит из цикла в то время как в lambda block.

StopMyThread() { 
    thread_running = false; 
    the_thread.join(); 
} 

Как я понимаю, я могу приостановить & резюме нить с помощью std::condition_variable как указывалось here в моем предыдущем вопросе.

Но есть недостаток, если я просто использовать std::atomic<bool>thread_running выполнить или не выполнить логику в GetData(), как показано ниже?

GetData() { 
    if (thread_running == false) 
     return; 
    //Some heavy logic 
} 

Будет ли это сжигать больше циклов процессора по сравнению с подходом с использованием std::condition_variable, как описаноhere?

+0

Честно говоря, я не понимаю 'the_thread.join();' который сразу после 'thread_running = false'. Я думаю, что установка 'thread_running' в' false', делает поток 'while' цикла потока завершенным, а затем выходит из потока. Что означает «.join» в этом случае? Особенно, когда поток использует * loop *. – Nawaz

+2

@Nawaz Вызов 'join' необходим, потому что OP не отсоединяет нить. Если он не присоединяется к мертвому потоку обратно в основную нить, основной ресурс (ы) потока будет протекать (как правило, в конечном итоге сбой приложения). –

+1

@Nawaz 'the_thread.join()' требуется из основного потока для ожидания остановки рабочего потока, который выходит из цикла GetData(). Если я не делаю '.join', тогда я сталкиваюсь с неопределенными сбоями приложений. поэтому всегда хорошо иметь хотя бы не полностью обязательный характер. Но мой вопрос здесь различен, как вы видите. Меня не беспокоит '.join' –

ответ

4

Переменная состояния полезна, когда вы хотите условно остановить другой поток или нет. Таким образом, у вас может быть всегда работающий «рабочий» поток, который ждет, когда он замечает, что он не имеет ничего общего с запуском.

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

Как правило, поток ответа пользовательского интерфейса никогда не должен блокироваться в состоянии неготовности из рабочих потоков.

struct worker_thread { 
    worker_thread(std::function<void()> t, bool play = true): 
    task(std::move(t)), 
    execute(play) 
    { 
    thread = std::async(std::launch::async, [this]{ 
     work(); 
    }); 
    } 
    // move is not safe. If you need this movable, 
    // use unique_ptr<worker_thread>. 
    worker_thread(worker_thread&&)=delete; 
    ~worker_thread() { 
    if (!exit) finalize(); 
    wait(); 
    } 
    void finalize() { 
    auto l = lock(); 
    exit = true; 
    cv.notify_one(); 
    } 
    void pause() { 
    auto l = lock(); 
    execute = false; 
    } 
    void play() { 
    auto l = lock(); 
    execute = true; 
    cv.notify_one(); 
    } 
    void wait() { 
    Assert(exit); 
    if (thread) 
     thread.get(); 
    } 
private: 
    void work() { 
    while(true) { 
     bool done = false; 
     { 
     auto l = lock(); 
     cv.wait(l, [&]{ 
      return exit || execute; 
     }); 
     done = exit; // have lock here 
     } 
     if (done) break; 
     task(); 
    } 
    } 
    std::unique_lock<std::mutex> lock() { 
    return std::unique_lock<std::mutex>(m); 
    } 
    std::mutex m; 
    std::condition_variable cv; 
    bool exit = false; 
    bool execute = true; 
    std::function<void()> task; 
    std::future<void> thread; 
}; 

или что-то подобное.

Это принадлежит нитке. Нить многократно запускает задачу, пока она находится в режиме play(). Если вы pause() в следующий раз заканчивается task(), рабочий поток останавливается. Если вы play() до окончания task(), то он не замечает pause().

Единственное ожидание - уничтожение worker_thread, где оно автоматически информирует рабочий поток, он должен выйти, и он ждет его завершения.

Вы можете указать вручную .wait() или .finalize(). .finalize() - это асинхронный режим, но если ваше приложение выключается, вы можете позвонить ему раньше и дать рабочий поток больше времени на очистку, в то время как основной поток очищает вещи в другом месте.

.finalize() не может быть отменено.

Код не проверен.

+0

Спасибо за такой яркий ответ. Следовательно, принимая этот ответ. –

1

Если я чего-то не упускаю, вы уже ответили на это в своем первоначальном вопросе: вы будете создавать и уничтожать рабочий поток каждый раз, когда это необходимо. Это может быть или не быть проблемой в вашем фактическом приложении.

+0

В моем первоначальном вопросе завершение остановки потока и начало нового - действительно проблема. Я мог даже увидеть отставание, когда я многократно нажимаю кнопку пользовательского интерфейса, чтобы воспроизвести/приостановить рабочий поток. Это коренная причина, по которой я нуждаюсь в альтернативах не останавливать поток. Кроме того, я читал, что очистка потока и запуск нового требует ОС для выделения ресурсов для потока, который является тяжелым делом. Правильно ? –

+0

Вы видели задержку из-за соединения всех порожденных нитей. Один нить не будет заметным – user1095108

+1

@ user1095108 Согласовать wrt Настольные компьютеры. Мой код также используется для мобильных телефонов и оборудования с низким энергопотреблением. И на мобильном телефоне средней дальности, если я специально нажимаю кнопку паузы на воспроизведение много раз, заставляя эту нить начинать и останавливаться, тогда небольшое отставание появляется один раз в 15 раз примерно –

1

Существует две различные проблемы, которые решаются, и это может зависеть от того, что вы на самом деле делаете. Одна из проблем: «Я хочу, чтобы мой поток работал, пока я не остановлю». Другой, по-видимому, относится к случаю: «У меня есть пара производителей/потребителей и хочу иметь возможность уведомлять потребителя, когда данные готовы». Метод thread_running и join хорошо работает для первого из них. Во-вторых, вы можете использовать мьютекс и условие, потому что вы делаете больше, чем просто использование состояния для запуска работы. Предположим, у вас есть vector<Work>. Вы защищаете это мьютексом, поэтому условие становится [&work](){ return !work.empty(); } или что-то подобное. Когда ожидание вернется, вы удерживаете мьютекс, чтобы вы могли убрать вещи и работать с ними. Когда вы закончите, вы вернетесь, чтобы подождать, выпустив мьютексы, чтобы продюсер мог добавить вещи в очередь.

Возможно, вы захотите объединить эти методы. У вас есть «обработанный» атом, который периодически проверяет все ваши потоки, чтобы узнать, когда выходить, чтобы вы могли присоединиться к ним. Используйте это условие, чтобы покрыть случай доставки данных между потоками.

+0

. Моя потребность не такая сложная, как сценарий вы объяснили. Мне нужно только играть или приостанавливать поток. Это оно.Следовательно, я думаю, что для выполнения тяжелой логики в 'GetData()' или для выполнения логики в 'GetData()' будет достаточно только 'std :: atomic' variable' thread_running', чтобы выполнить тяжелую логику ' –

+0

. Моя проблема - это не производитель потребительская проблема. Это просто самостоятельный worker_thread, который необходимо временно приостановить и возобновить. Считаете ли вы, что мне все еще нужно использовать «std :: condition_variable» для достижения этого? –

+1

Вам нужно подождать() или нить без необходимости будет записывать циклы процессора – user1095108

Смежные вопросы