2016-05-19 2 views
0

Я играю, чтобы попытаться дать тайм-аут в тему. Я пытаюсь использовать std::condition_variable::wait_for, но это ведет себя по-другому, чем я ожидал.условие переменная таймаут непонятно

Вот мой простой код:

#include <iostream> 
#include <thread> 
#include <chrono> 
#include <mutex> 
#include <condition_variable> 

std::mutex myMutex; 

void waitSec(int seconds) 
{ 
    std::cout <<"t1 start" <<std::endl; 
    std::unique_lock<std::mutex> lock(myMutex); 
    std::this_thread::sleep_for(std::chrono::seconds(seconds)); 
    std::cout <<"t1 end" <<std::endl; 
} 

int main(void) 
{ 
    // launch thread that sleeps 10s 
    std::thread t1(waitSec,10); 
    std::this_thread::sleep_for(std::chrono::seconds(1)); 

    // wait for lock during max 1 second 
    std::condition_variable* conditionVariable = new std::condition_variable(); 
    std::cout << "before" << std::endl; 
    std::unique_lock<std::mutex> lock(myMutex); 
    conditionVariable->wait_for(lock,std::chrono::seconds(1)); 
    std::cout << "after" << std::endl; 

    t1.join(); 
} 

Я ожидаю, что поток T1 до бездействует в течение 10 секунд. Поскольку я даю 1с таймауту wait_for в основном потоке, я бы ожидал, что after напечатан до t1 end.

Что происходит, так это то, что t1 работает в течение 10 секунд (печать t1 end) и всего 1 с после печати after.

Можете ли вы объяснить мне, почему он ведет себя так и что мне нужно сделать, чтобы мой тайм-аут выполнялся как тайм-аут?

+0

похоже, что t1 заблокирован myMutex, и основной поток должен ждать, пока он его не выпустит – DAG

ответ

2

Это не имеет никакого отношения к condition_variable. У вас есть unique_lock в waitSec до вашего 10 секунд спать, так что никто не сможет получить мьютекс в течение 10 секунд, так что эта линия:

std::unique_lock<std::mutex> lock(myMutex); 

будет ждать до конца waitSec и выпуска mutex по unique_lock деструктор.

Вам необходимо создать condition_variable перед тем, как любой другой поток заблокирует ваш мьютекс.

+0

спасибо ... это помогло ... поэтому я создал блокировку в начале основного. Тогда, поскольку это автоматически блокирует мьютекс, я называю разблокировку. Благодаря! – Stefano

+0

@Stefano Вы не должны разблокировать мьютексы вручную, 'wait_for' (и другой' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' condition_variable/ожидание). – Holt

+0

nono ... это было из-за того, что я хотел создать блокировку в главном, не блокируя ее ... я только что обнаружил, что могу создать это, используя второй параметр std :: defer_lock – Stefano

1

В этом случае вам не нужно использовать условные переменные, так как вы просто спите 1 секунду в основном потоке после начала t1.

Что произошло на самом деле, что вы заблокировали мьютекс в потоке 1:

std::unique_lock<std::mutex> lock(myMutex); 

затем в главном потоке вы пытаетесь заблокировать этот семафор снова (после возвращения из 1 второй сна):

std::cout << "before" << std::endl; 
std::unique_lock<std::mutex> lock(myMutex); 

Основной поток сможет получить этот замок только после завершения t1 (~ 9 секунд, всего 10 секунд). Тогда программа будет печатать «до» и успешно получить блокировку и будет ждать любом случае в течение 1 секунды, потому что:

conditionVariable->wait_for(lock,std::chrono::seconds(1)); 

И там никто не может проснуться (уведомить) основной поток от ожидания, так что это будет просто спокойно спать в течение 1 секунды, а затем печатает «после»

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

Если вам нужно подождать одну секунду или пока нить не закончится, вы можете сделать что-то вроде этого (обратите внимание, что этот код не идеален, просто чтобы показать основную идею):

std::condition_variable cv; 

void worker_thread() 
{ 
    // do something 
    // ... 
    cv.notify_one(); 
} 

int main() { 
    std::thread t1(worker_thread); 

    std::cout << "before" << std::endl; 
    std::unique_lock<std::mutex> lock(myMutex); 
    cv.wait_for(lock,std::chrono::seconds(1)); 
    std::cout << "after" << std::endl; 
} 
+0

right ... Чего я хочу достичь просто давая тайм-аут процедуре t1 ... если после того, как 1s не закончил, основной поток должен просто продолжить. Вы знаете, как я мог это сделать? – Stefano

+0

@Stefano Я обновлю ответ –

1

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

в основной() вы пытаетесь получить мьютекс в 1 строке здесь:

std::unique_lock<std::mutex> lock(myMutex); 
conditionVariable->wait_for(lock,std::chrono::seconds(1)); 

Однако ваш поток t1 держит мьютекс здесь:

std::unique_lock<std::mutex> lock(myMutex); 
std::this_thread::sleep_for(std::chrono::seconds(seconds)); 

Таким образом, код в основной() не может продолжаться до тех пор, пока не будет отключен мьютекс в функции waitSec, запущенный потоком t1, который происходит, когда функция заканчивается.

Не храните мьютексы во время сна.

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