Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: Гурман от Август 25, 2011, 22:27



Название: Надо дождаться двух сигналов
Отправлено: Гурман от Август 25, 2011, 22:27
В основном потоке уйти в ожидание, пока два параллельных потока не выдадут свои сигналы. Оба, то есть, надо получить оба сигнала, и только после этого продолжить выполнение. Хотел было сделать так:

основной поток:

Код:
    QMutex m( QMutex::Recursive );
    m.lock();
    m.lock();
    QWaitCondition w;
    w.wait( &m );

соответственно в слотах для сигналов от потоков 1 и 2, в обоих:

Код:
    w.wakeOne();

но облом, в мануале написано, что w.wait() освободит кумутекс, если он рекурсивный - проверил, действительно, wait проскакивает

вот пока что-то не могу придумать изящное решение на ночь глядя, оно наверняка очень простое, может кто подскажет? хотелось бы, чтобы без перезапуска ожидания в ловушках сигналов, так я без подсказки могу сделать


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 25, 2011, 22:43
QSemaphore?


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 25, 2011, 22:51
ну и как его в главном потоке приложения использовать? задерживать два других потока мне не нужно - они присылают сигналы, когда им надо, через QueuedConnection, и как-то сами крутятся

то есть, в классе, который в главном потоке должен их подождать, есть два слота приемников этих сигналов

как тут QSemaphore прикрутить? кто и как делает wait()? особенно, с учетом, что это все находится хоть и в главном потоке, но не в основном приложении - это в плагине


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 25, 2011, 22:54
Про семафор это скорее были мысли в слух...
"wait" делает он сам при попытке захватить ресурс сверх лимита.


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 25, 2011, 23:00
Цитировать
Про семафор это скорее были мысли в слух...

аааа...

Цитировать
wait делает он сам при попытке захватить ресурс сверх лимита.

это я понимаю, поэтому с семафорами тоже ерунда получается - он не может остановиться до тех пор, пока ДВА ресурса не освободятся, он побежит дальше, как только освободится один


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 25, 2011, 23:09
Основной поток:
Код
C++ (Qt)
QMutexLocker locker( &m );
while( numThread > 0 )
w.wait( &m );
 

Изначально numThread = 2, каждый поток при завершении должен сделать декремент под защитой мутекса m, ну и wakeOne.



Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 25, 2011, 23:19
Основной поток:
Код
C++ (Qt)
QMutexLocker locker( &m );
while( numThread > 0 )
w.wait( &m );
 

Изначально numThread = 2, каждый поток при завершении должен сделать декремент под защитой мутекса m, ну и wakeOne.



надо разобраться...


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 25, 2011, 23:22
Так тут никакая остановка главного потока не подойдет, т.к. нужно постоянно крутить очередь событий для обработки сигналов.  ::)


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 25, 2011, 23:24
QWaitCondition на очередь событий не влияет


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 25, 2011, 23:25
Влияет, wait поток останавливает! Иначе в нем смысла не было бы. :)


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 25, 2011, 23:35
он останавливает только главный поток, но не обработку событий, иначе у меня не работали бы "двунаправленные сокет-слоты", то есть, "сигналы с возвратом значения", вот такие:

Код:
void class::sender( int somevalue )
{
    QMutex mutex;
    mutex.lock();
    emit send( somevalue );  // послали другому объекту сигнал
    waiter.wait( &mutex, 1000 ); // и ждем его ответа
    mutex.unlock();
    process( 0.0 );
}

// это слот, в который другой объект присылает ответ
void class::receiver( float returnedvalue ) // SLOT
{
    waiter.wakeAll();
    process( returnedvalue );
}
кстати, тогда и таймер бы не работал у самого wait, и интерфейс бы блокировался, но этого не происходит


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 25, 2011, 23:40
а зачем там QMutexLocker ? какой в нем тут смысл?


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 25, 2011, 23:44
wait таймеры не использует.
А зачем ты у wait таймаут используешь? А если его убрать?
Ну и твой кусок кода ничего не показывает, ни очередь событий, ни "сигналы с возвратом значений".

Главное! wait останавливает поток, наглухо. Останавливается все, включая очередь сообщений. Это то, для чего он предназначен. ;)


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 25, 2011, 23:45
а зачем там QMutexLocker ? какой в нем тут смысл?
Безопасно залочить мьютекс.


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 25, 2011, 23:47
я строку добавил, которая показывает, зачем таймаут - если другой объект не отозвался, то считается, что результат 0.0

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

Цитировать
твой кусок кода ничего не показывает, ни очередь событий, ни "сигналы с возвратом значений"

так показывает?

Код:
Объект1::<сигнал>send(somevalue) --> Объект2::<слот>recevie(somevalue)...чего-то сделал...Объект2::<сигнал>sendresult(returnedvalue) --> Объект1::<слот>receiver(returnedvalue)

оба объекта в главном потоке, все работает на ура, несмотря на wait()

если соединения нет (второй объект в другом плагине, а он может быть не загружен), то через 1 секунду получаю 0.0, если соединение есть, то ответ от второго объекта

а думаю, wait где-то внутри упирается в qnoop(), который в основном цикле ожидания используется

Цитировать
Безопасно залочить мьютекс.

а какая может быть опасность?


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 25, 2011, 23:51
Вставь вот такую строку в разные места и посмотри в каких потоках точно работает код. Что то мне подсказывает, что ты будешь удивлен результатом. ;)
Код
C++ (Qt)
qDebug() << QThread::currentThreadId();
 


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 25, 2011, 23:53
а какая может быть опасность?

Код
C++ (Qt)
{
m.lock();
...
if( условие )
{
return; // Мутекс m останется залоченным. С QMutexLocker такое не возможно.
}
 
m.unlock();
}
 


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 26, 2011, 00:05
Вставь вот такую строку в разные места и посмотри в каких потоках точно работает код. Что то мне подсказывает, что ты будешь удивлен результатом. ;)

а, не... вспомнил... это используется действительно, только с объектом, работающим в другом потоке

уже час ночи, спать пора...

Цитировать
Мутекс m останется залоченным. С QMutexLocker такое не возможно.

ну в моем случае такого не будет


Название: Re: Надо дождаться двух сигналов
Отправлено: Igors от Август 26, 2011, 10:16
Ну просто "дождаться" нет проблем
Код
C++ (Qt)
for (int i = 0; i < 2; ++i)
theSemaphore.acquire();
// полагаем что др. нитки сделают theSemaphore.release
 
Но это остановит нитку полностью, ничего принять она не сможет пока ее с семафора не снимут др. нитки.

Поэтому выглядит так что ничего останавливать/синхронизировать не надо. Крутится себе нитка в eventLoop - ну и пусть. Когда оба нужных сигнала приняты (напр прjверить по if) - начинается какое-то действие


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 26, 2011, 12:08
Цитировать
это остановит нитку полностью, ничего принять она не сможет пока ее с семафора не снимут др. нитки.

не, это ерунда

Цитировать
выглядит так что ничего останавливать/синхронизировать не надо.

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

BRE в конце предыдущей страницы предложил решение, похожее на то, что надо, только там счетчик при приеме сигналов надо не мутексом m защищать, а еще одним мутексом, m занят ожидателем w

пока руки не дошли реализовать, другое дело подвернулось, но идея правильная


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 26, 2011, 12:11
только там счетчик при приеме сигналов надо не мутексом m защищать, а еще одним мутексом, m занят ожидателем w
Разберись как работают условные переменные. :)
m ничем не занят. wait разлочит этот мьютекс при остановке и залочит его заново при просыпании.


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 26, 2011, 12:27
Цитировать
wait разлочит этот мьютекс при остановке и залочит его заново при просыпании

где об этом написано?


Название: Re: Надо дождаться двух сигналов
Отправлено: Igors от Август 26, 2011, 12:36
два независимых плагина, работающих в отдельных потоках, проводят инициализацию и сообщают об этом сигналами, основной поток не может продолжать работу дальше, пока они этого не сделают
Ну значит главная нитка должна принять эти сигналы и блокировать ее нельзя. А "остановить работу" можно напр так
Код
C++ (Qt)
while (true) {
if (plugin1Ready && plugin2Ready) break;
QThread::yieldCirrentThread();
QApplication::processEvents();
}
 


Название: Re: Надо дождаться двух сигналов
Отправлено: BRE от Август 26, 2011, 12:58
где об этом написано?
Поищи в нете описание работы pthread_cond_wait.

Добавил: ну и в документации на QWaitCondition::wait все подробно расписано.


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 26, 2011, 12:58
Цитировать
while (true) {
 if (plugin1Ready && plugin2Ready) break;
 QThread::yieldCirrentThread();
 QApplication::processEvents();
}

а вот это уже ерунда - загрузка процессора на 60%, плюс невозможность обработать ситуацию, когда один плагин не загружен, в варианте BRE такая обработка делается просто добавлением таймаута в wait( &m, 3000 ) - через 3 секунды, если плагины не проинициализировались (выясняется проверкой на то, что numThread по прежнему равен 2), катим дальше, выдаем сообщение об ошибке плагина, и нормально завершаемся


Название: Re: Надо дождаться двух сигналов
Отправлено: Igors от Август 26, 2011, 14:42
в варианте BRE такая обработка делается просто добавлением таймаута в wait( &m, 3000 ) - через 3 секунды, если плагины не проинициализировались (выясняется проверкой на то, что numThread по прежнему равен 2), катим дальше, выдаем сообщение об ошибке плагина, и нормально завершаемся
Можно и так, но тогда без сигналов, и на 3 сек UI заморожен

а вот это уже ерунда - загрузка процессора на 60%, плюс невозможность обработать ситуацию, когда один плагин не загружен,
Про флажки для processEvents почитайте - чтоб не загружать процессор. И вообще - лучше сначала разобраться а потом резво кричать "ерунда-ерунда"  :)
Умолкаю..


Название: Re: Надо дождаться двух сигналов
Отправлено: Гурман от Август 26, 2011, 14:46
Цитировать
тогда без сигналов

с сигналами, они из других потоков приходят, и нормально отрабатывают



Название: Re: Надо дождаться двух сигналов
Отправлено: SASA от Август 26, 2011, 15:36
Цитата: Гурман link=topic=19234.msg129815#msg129815
ну в моем случае такого не будет
[/quote
Чтоб не думать о случаях, лучше всегда пользоваться QMutexLocker.

По теме. А не проще завести флаг типа intitOk. (код из головы)
Код:
enum intit
{
     notInit = 0,
     firstPlugOk = 1,
     secondPlugOk = 2,
     bothPlugOk = firstPlugOk | secondPlugOk
}
...
intit initOk = notInit;
...
while(initOk == bothPlugOk )
{
   // например, processEvent
}
...
if (!startInitPlug1()) // нет плагина
{
   firstInitFinished();
   parm = defVal;
}
....
void firstInitFinished()
{
    intit = intit |firstPlugOk;
}
Мы же не потоки ждём, а инициализацию.