Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: Гурман от Ноябрь 18, 2014, 12:45



Название: (РЕШЕНО) QWaitCondition::wait() в разных версиях
Отправлено: Гурман от Ноябрь 18, 2014, 12:45
Проект из-за требований совместимости продолжается на Qt 4.7. В описании QWaitCondition::wait( QMutex*, ... ) четко написано

Цитировать
If mutex is not in a locked state, this function returns immediately.

То есть, подразумевается нормальный возврат. Вместо этого, если мутекс не залочен, то падает по assert (там внутри сразу простая проверка на owner нити, а у нелоченного мутекса owner == 0).

Вопрос - в более новых версиях Qt как? На незалоченном мутексе QWaitCondition::wait() тоже будет падать, или всё-таки, как по описаню, проскочит и вернет false (кстати, про возврат в описании для этого случая ничего не сказано)? У кого последние версии Qt, плз, посмотрите в исходники.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: vulko от Ноябрь 18, 2014, 14:16
Цитировать
bool QWaitCondition::wait ( QMutex * mutex, unsigned long time = ULONG_MAX )

Releases the locked mutex and waits on the wait condition. The mutex must be initially locked by the calling thread. If mutex is not in a locked state, this function returns immediately. If mutex is a recursive mutex, this function returns immediately. The mutex will be unlocked, and the calling thread will block until either of these conditions is met:

Another thread signals it using wakeOne() or wakeAll(). This function will return true in this case.
time milliseconds has elapsed. If time is ULONG_MAX (the default), then the wait will never timeout (the event must be signalled). This function will return false if the wait timed out.
The mutex will be returned to the same locked state. This function is provided to allow the atomic transition from the locked state to the wait state.

qt 4.8.5, linux.
мьютекс не лочу, отлично работает.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Igors от Ноябрь 18, 2014, 18:51
Ассыстент 5.3.2
Цитировать
bool QWaitCondition::wait(QMutex * lockedMutex, unsigned long time = ULONG_MAX)
Releases the lockedMutex and waits on the wait condition. The lockedMutex must be initially locked by the calling thread. If lockedMutex is not in a locked state, the behavior is undefined.

И без разницы падает или нет - делать так нет смысла. Возможно Вы хотите написать что-то типа
Код
C++ (Qt)
if (CheckSomething())
condition.wait(&mutex);

Но это не будет работать правильно если мутекс не залочен


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Гурман от Ноябрь 18, 2014, 21:49
Ассыстент 5.3.2
Цитировать
bool QWaitCondition::wait(QMutex * lockedMutex, unsigned long time = ULONG_MAX)
Releases the lockedMutex and waits on the wait condition. The lockedMutex must be initially locked by the calling thread. If lockedMutex is not in a locked state, the behavior is undefined.

И без разницы падает или нет - делать так нет смысла. Возможно Вы хотите написать что-то типа
Код
C++ (Qt)
if (CheckSomething())
condition.wait(&mutex);

Но это не будет работать правильно если мутекс не залочен

Нет, я хотел написать

Код:
if( ! condition.wait(&mutex) )  // по описанию 4.7 должно быть false, если мутекс не залочен, но тут падает
    mutex.lock();

То есть, если мутекс не был залочен, то его надо залочить. А если был, то ждать - при освобождении перед выходом wait() залочит его сам, в 4.7 так и работает, и это описано. Кстати, в 5.3 тоже так? При выходе из wait(&mutex), mutex будет в залоченном состоянии?


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Old от Ноябрь 18, 2014, 22:03
Вы не правильно представляете работу условных переменных.
Смысл их в том, что вы лочите мутекс, проверяете доступность ресурса и если ресурс не доступен, то уходите на wait. Он разлочивает мутекс (для того чтобы другие нити могли его заполнить/сделать доступным) и усыпляет нить.
Когда происходит побудка, то нить просыпается в wait, который лочит мутекс и завершается. Мы может при залоченном мутексе опять проверить ресурс на доступность.

На форуме не раз поднималась тепа по услоаным переменным, в которых детально все говорилось.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: vulko от Ноябрь 18, 2014, 22:20
Вы не правильно представляете работу условных переменных.
Смысл их в том, что вы лочите мутекс, проверяете доступность ресурса и если ресурс не доступен, то уходите на wait. Он разлочивает мутекс (для того чтобы другие нити могли его заполнить/сделать доступным) и усыпляет нить.
Когда происходит побудка, то нить просыпается в wait, который лочит мутекс и завершается. Мы может при залоченном мутексе опять проверить ресурс на доступность.

На форуме не раз поднималась тепа по услоаным переменным, в которых детально все говорилось.

Если мьютекс отдельный для CV, то можно его и не лочить. Я именно так и делаю. Для приостановки потока самое оно.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Old от Ноябрь 18, 2014, 22:54
Я не вижу смысла в условных переменных без защищаемого ресурса и связанного с ним мутекса.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: vulko от Ноябрь 18, 2014, 23:14
Я не вижу смысла в условных переменных без защищаемого ресурса и связанного с ним мутекса.

Например контроль работы потока. Приостановить и продолжить работу.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Old от Ноябрь 18, 2014, 23:18
Например контроль работы потока. Приостановить и продолжить работу.
Не очень понятно, что это за контроль? Для чего он нужен?
И я бы все равно сделал переменную bool pause, защитил ее мутексом и использовал ее для организации приостановки - это и был бы защищаемый ресурс.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Гурман от Ноябрь 18, 2014, 23:54
Вы не правильно представляете работу условных переменных.
Смысл их в том, что вы лочите мутекс, проверяете доступность ресурса и если ресурс не доступен, то уходите на wait. Он разлочивает мутекс (для того чтобы другие нити могли его заполнить/сделать доступным) и усыпляет нить.
Когда происходит побудка, то нить просыпается в wait, который лочит мутекс и завершается. Мы может при залоченном мутексе опять проверить ресурс на доступность.

На форуме не раз поднималась тепа по услоаным переменным, в которых детально все говорилось.

Судя по этому описанию, я как раз всё понимаю правильно. И защищаемый ресурс есть, конечно же. Нет же никакого смысла в синхронизации потоков, если им не надо получать доступ к одному и тому же ресурсу, но ни в коем случае не одновременно. Просто если бы wait(&mutex) возвращал false при разлоченном мутексе, это было бы удобной информацией о доступности ресурса, значит его можно захватить (сделать mutex.lock()) и обрабатывать. А если ресурс недоступен, то сразу автоматом wait() становился бы в ожидание, и захватывал бы ресурс, как только тот освободится. Без этого приходится заводить, устанавливать, сбрасывать и проверять свой собственный флаг доступности ресурса, поскольку другого способа проверить это не существует. То есть, можно было бы удобнее инструмент иметь, но увы...


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Old от Ноябрь 19, 2014, 00:05
Перед тем как заснуть на wait, мы должны проверить, а есть ли для нас данные для обработки или нет.
Для этого мы лочим мутекс и лезем в наш ресурс (он то общий). Если данные есть, то при все еще запертом мутексе мы их достаем, разлочиваем мутекс и начинаем их обрабатывать. А вот если данных нет, то мы уходим на wait (мутекс все еще заперт) и уже он его разлочит.
Не очень понятно, для чего его освобождать перед wait и все это проверять + лочить руками?
Использовать мутекс в качестве флага плохая идея, тогда уж лучше взять семафор.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: vulko от Ноябрь 19, 2014, 09:04
Например контроль работы потока. Приостановить и продолжить работу.
Не очень понятно, что это за контроль? Для чего он нужен?
И я бы все равно сделал переменную bool pause, защитил ее мутексом и использовал ее для организации приостановки - это и был бы защищаемый ресурс.

Ессессно с bool pause. В соседнем топике уже приводил такой код.
Контроль простой. Есть поток с while(running) { }. Нужно его приостановить и продолжить работу. Например поток шлет данные. Либо поток хэндлить входящие данные. Много ситуаций.
Иногда он не нужен, но зачем его прибивать, если можно просто поставить в wait на время и не инициализировать заново.

Код:
void MyThread::run() {
    // init
    mContinue = new QMutex;
    mWaitCondition = new QWaitCondition;

    while(running) {
        lock();
        // do stuff
        unlock();

        if (pause) {
            mWaitCondition->wait(mContinue);
        }

        exec();
    }
}

void MyThread::pause() { pause = true; }

void MyThread::resume() {
    if (pause) {
        pause = false;
        mWaitCondition->wakeAll();
    }
}


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Old от Ноябрь 19, 2014, 09:11
Вот для доступа к pause и нужен мутекс, и именно его нужно использовать в wait.
А почему он будет залочен при его передаче в wait я описал в предыдущем посте.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: vulko от Ноябрь 19, 2014, 09:16
Вот для доступа к pause и нужен мутекс, и именно его нужно использовать в wait.
А почему он будет залочен при его передаче в wait я описал в предыдущем посте.

Если много потоков могут делать pause/wake, то конечно нужен.
А если один, то мьютекс передаваемый в wait() может никем никогда не использоваться.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Old от Ноябрь 19, 2014, 09:23
Если много потоков могут делать pause/wake, то конечно нужен.
А если один, то мьютекс передаваемый в wait() может никем никогда не использоваться.
Ну их как минимум два: один останавливающий, второй - останавливаемый.
И лучше сразу все делать правильно (никаких сложностей в этом нет), это сейчас поток один, а завтра их может быть уже 100500.


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: vulko от Ноябрь 19, 2014, 09:39
Если много потоков могут делать pause/wake, то конечно нужен.
А если один, то мьютекс передаваемый в wait() может никем никогда не использоваться.
Ну их как минимум два: один останавливающий, второй - останавливаемый.
И лучше сразу все делать правильно (никаких сложностей в этом нет), это сейчас поток один, а завтра их может быть уже 100500.


Насчет 2-х потоков не соглашусь.
Мьютекс тут совсем не обязателен, т.к. если pause выставлен в true из другого потока, не далее как на следущей итерации поток зайдет внутрь if(pause) и встанет в wait, до тех пор пока первый поток не вызовет resume().


Правильно сделать лучше, согласен, но тут эксперт англ. из 90-х забыл уточнить условия... :)))
Он кстати вообще говорил, что "поток с мьютексом без мьютекса" нельзя использовать, т.к. "мьютекс может быть, а может и не быть". И вообще завтра может быть уже сегодня...)))
А тут и мьютекс и CV решил сразу нашлёпать...)))


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Igors от Ноябрь 19, 2014, 10:06
Просто если бы wait(&mutex) возвращал false при разлоченном мутексе, это было бы удобной информацией о доступности ресурса, значит его можно захватить (сделать mutex.lock()) и обрабатывать.
Не можно.

Код:
if( ! condition.wait(&mutex) )  // по описанию 4.7 должно быть false, если мутекс не залочен, но тут падает
    mutex.lock();
Пусть condition.wait вернуло false, но это значит что ресурс доступен в данный момент (до выполнения mutex.lock()). А когда выполнится mutex.lock(), доступность может быть уже совсем другая

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

Схемв (Q)WaitCondition всегда одна и та же, ее надо тупо переписывать  


Название: Re: QWaitCondition::wait() в разных версиях
Отправлено: Гурман от Ноябрь 19, 2014, 14:55
Использовать мутекс в качестве флага плохая идея, тогда уж лучше взять семафор.

Не вполне понятно, при чем тут семафор, который для разруливания M запросов к N ресурсам. Ну да ладно...

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