Russian Qt Forum
Ноябрь 23, 2024, 16:28 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Синхронное обращение через сигнал слоты и возврат результата расчета после emit  (Прочитано 28912 раз)
Denjs
Гость
« : Октябрь 13, 2010, 15:48 »

  • Как проверить что твой сигнал получен и обработан?
  • Как дождаться момента когда твой сигнал обработан?
  • Как получить значения, рассчитанные в приемном слоте, к которому подключен твой сигнал? (ведь ты не знаешь куда он даже подключен?)
  • Как избавиться от "лишнего груза знаний" о том, кто должен рассчитывать нужные тебе параметры?
Эти и похожие проблемы решает класс t_returnSyncObject.
У вас есть класс-компонента которая рассчитывает по запросу какие-то значения, а есть классы которым необходимы данные параметры. При этом ни первые ни вторые не желают ничего знать друг о друге. Сигнал-слоты с дополнительным объектом в сигнатуре сигнала помогут связать такие объекты.

В каком-то смысле, это "внутрипрограммная" реализация идей SOA - "через сигнал-слот вызывается расчет каких-либо значений, и ни источник ни приемник не знают друг о друге ничего".

применение и описание - см в коде.

PS: Все вроде потоко-безопасно и подходит для межпоточного взаимодействия, но, признаться пока специально не проверял ))))

PS: В качестве бонуса - "кроссплатформенный Sleep" - static void SleepMS_qt( unsigned int time)

------------------------------------------------
Исходный код класса:
t_returnSyncObject.h
Код:
/*
           UTF-8 encoded file !!!!
*/
#ifndef T_RETURNSYNCOBJECT_H
#define T_RETURNSYNCOBJECT_H

#include <QObject>
#include <QMutexLocker>
#include <QVariant>

/*
@Denjs 2010
Public Domain licensed ("Общественное достояние")
QT 4.6 tested
*/

/*
Класс презназначенный для реализации
синхронного взаимодействия
через сигнал-слоты.

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


Экземпляр класса передается с сигналом,
а на приемной стороне вызывается метод
bool t_returnSyncObject::done()
для сигнализации того, что обработка закончена.
для отслеживания случая, когда сигнал подключен
к более чем 1 слоту, вызывается
bool t_returnSyncObject::done()


На исходной стороне, после испускания сигнала,
вызывается
bool t_returnSyncObject::waitForDone(int timeOut);
метод задерживает управление до момента,
пока не вызван .done() или истечения таймаута.

Для передачи параметров используется
t_returnSyncObject::set_result(QVariant retult)

*/


class t_returnSyncObject : public QObject
{
    Q_OBJECT

public:
    explicit t_returnSyncObject(QObject *parent = 0);

signals:

public slots:
    bool done();
    bool isDone();

    bool reset();
    bool waitForDone(int _timeOut);
    bool setResult(QVariant __result);
    QVariant result();

private:
    mutable QMutex mutex;
    bool    _done;
    QVariant _result;
};

//================================================================================
//--------------------------------------------------------------------------------
// Linux :
#if defined (Q_WS_X11)
        /* nothing needs to be included here yet...*/
// Win :
#elif defined (Q_WS_WIN)
        // for Sleep() :
        #include <windows.h>
// Mac:
#elif defined (Q_WS_MAC)
        /* NOT SUPPORTED THIS VERSION */
#else
        /* NOT SUPPORTED THIS VERSION */
#endif
//--------------------------------------------------------------------------------

static void SleepMS_qt( unsigned int time)
{
        // Linux:
        #if defined (Q_WS_X11)
                        usleep(time*1000);//sleep in millisecinds
        // Win:
        #elif defined (Q_WS_WIN)
                        Sleep(time);//WIN  2008.02.19
        // Mac:
        #elif defined (Q_WS_MAC)
                        // NOT SUPPORTED THIS VERSION
        #else
                        // NOT SUPPORTED THIS VERSION
        #endif
        return;
};


#endif // T_RETURNSYNCOBJECT_H

t_returnSyncObject.cpp
Код:
/*
           UTF-8 encoded file !!!!
*/
#include "t_returnSyncObject.h"

//============================================================================
t_returnSyncObject::t_returnSyncObject(QObject *parent) :
    QObject(parent)
 {
    reset();
 };

//============================================================================
bool t_returnSyncObject::done()
 {
    QMutexLocker locker(&mutex);
    _done=true;
    return _done;
 };

//============================================================================
bool t_returnSyncObject::isDone()
 {
    QMutexLocker locker(&mutex);
    return _done;
 };

//============================================================================
bool t_returnSyncObject::waitForDone(int _timeOut)
 {
    int timePassed=0;
    int stepTimeout=200;
    if ( (!isDone()) && (timePassed<_timeOut) )
        {
            SleepMS_qt(stepTimeout);
            //sleep
            timePassed += stepTimeout;
        };
    return isDone();
 };

//============================================================================
bool t_returnSyncObject::setResult(QVariant __result)
 {
    QMutexLocker locker(&mutex);
    _result = __result;
    return _done;
 };

//============================================================================
QVariant t_returnSyncObject::result()
 {
    return _result;
 };

//============================================================================
bool t_returnSyncObject::reset()
 {
    QMutexLocker locker(&mutex);
    _result = QVariant();
    _done = false;
    return true;
 };




------------------------------------------------
Исходный код приложения:

код вызывающей стороны,
у вызывающей стороны есть сигнал
void mySignal(t_returnSyncObject *syncResultObject)
который подключен к слоту, который описан в последнем листинге.
Код:
            t_returnSyncObject *syncResultObject=new t_returnSyncObject(this);

            emit mySignal(syncResultObject);
            bool rez=syncResultObject->waitForDone(5000);// ждем не более 5 секунд
            if (rez==false) //таймаут...
                {
                    qDebug() << "Таймаут получения ответа от приемной стороны..";
                    return -1;
                };
            //теперь проверим что нам ответил слот-сервис
            QString rezStr=syncResultObject->result().toString();

код слота:
Код:
void myClass2::mySlot(t_returnSyncObject *syncResultObject)
{
    if (syncResultObject==0) return;
    if (syncResultObject->isDone()) return;

    QString rez="Обработано!";

    syncResultObject->setResult(QVariant(rez));
    syncResultObject->done();
    return;
}

Обсуждаем, комментируем, в меру ругаем, улучшаем ?  Улыбающийся

PS: из улучшений я пока могу предложить только добавить обработку сообщений в цикле ожидания (bool t_returnSyncObject::waitForDone(int _timeOut)) - что бы не "замирать" в случае чего...

PS: также можно попытаться развить возврат множества значений в класс-источни-сигнала. Но это на личное усмотрение)
« Последнее редактирование: Октябрь 14, 2010, 00:45 от Denjs » Записан
Akon
Гость
« Ответ #1 : Октябрь 13, 2010, 19:08 »

похоже на visitor.

методы isDone() и result() должны быть const.
поле _done должно быть volatile.
waitForDone() в общем случае должен принимать callback - пользователь твоего класса сам решит чем нагрузить. обработка цикла сообщений это частный случай.

Ну и стиля Qt лучше придерживаться, имхо.













 
Записан
Denjs
Гость
« Ответ #2 : Октябрь 13, 2010, 22:20 »

2 Akon : ээээ.... вы кажется не поняли зачем нужен класс или как я его применяю.

методы isDone() и result() должны быть const.
эти методы нужны для возврата данных именно в тот экземпляр объекта который испустил слот, именно в то место и время, где он был испущен. И относятся они именно к тому экземпляру t_returnSyncObject, ссылка на который нам прислана с сигналом.
« Последнее редактирование: Октябрь 13, 2010, 22:27 от Denjs » Записан
spectre71
Гость
« Ответ #3 : Октябрь 13, 2010, 22:47 »

  • Как проверить что твой сигнал получен и обработан?
  • Как дождаться момента когда твой сигнал обработан?
  • Как получить значения, рассчитанные в приемном слоте, к которому подключен твой сигнал? (ведь ты не знаешь куда он даже подключен?)
  • Как избавиться от "лишнего груза знаний" о том, кто должен рассчитывать нужные тебе параметры?

1) Например Qt::DirectConnection, или выставить флаг, или послать обратный сигнал, или ...
2) Например Qt::DirectConnection, или выставить флаг, или послать обратный сигнал, или ...
3) поскольку ты где-то делаешь прямое подключение, легко сделать там и обратное подключение - самое примтивное. Или ...
4) Интерфейсы, менеджеры интерфейсов и прочая паттерная хрень Улыбающийся Или ...
« Последнее редактирование: Октябрь 13, 2010, 22:48 от Spectre » Записан
Denjs
Гость
« Ответ #4 : Октябрь 14, 2010, 00:10 »

1) Например Qt::DirectConnection, или выставить флаг, или послать обратный сигнал, или ...
2) Например Qt::DirectConnection, или выставить флаг, или послать обратный сигнал, или ...
3) поскольку ты где-то делаешь прямое подключение, легко сделать там и обратное подключение - самое примтивное. Или ...
4) Интерфейсы, менеджеры интерфейсов и прочая паттерная хрень Улыбающийся Или ...
Qt::DirectConnection при межпотоковом взаимодействии всегда(!) становится Qt::QuotedConnection;

выставлять флаг? где? и куда смотреть? связанные объекты даже ссылок не имеют друг на друга....;

обратное подключение?! и что? отправив сигнал-запрос, я должен ловить результат в другом слоте, а в итоге - "одну простую функцию" разносить на несколько слотов и фактически - "делать громоздкую реализацию машины состояний" вместо одного простого линейного алгоритма который должен запросить несколько фнешних параметров?? не-не-нееее)

"Интерфейсы, менеджеры интерфейсов и прочая паттерная хрень"? дада) именно хрень. тем более в условиях когда сигнатура функции фактически и является описанием мини-интерфейса.
« Последнее редактирование: Октябрь 14, 2010, 00:15 от Denjs » Записан
Kolobok
Гость
« Ответ #5 : Октябрь 14, 2010, 00:29 »

Qt::DirectConnection при межпотоковом взаимодействии всегда(!) становится Qt::QuotedConnection;

В этом случае можно использовать Qt::BlockingQueuedConnection.
Записан
voral
Гость
« Ответ #6 : Октябрь 14, 2010, 00:31 »

Хм.. Может у меня такой особой необходимости не возникало идти таким путем....

А почему не через sender()?
Записан
Denjs
Гость
« Ответ #7 : Октябрь 14, 2010, 00:42 »

Qt::DirectConnection при межпотоковом взаимодействии всегда(!) становится Qt::QuotedConnection;

В этом случае можно использовать Qt::BlockingQueuedConnection.
Цитировать
This connection type should only be used where the emitter and receiver are in different threads. Note: Violating this rule can cause your application to deadlock.
т.е. в таком случае - я должен постоянно следить за тем, что в каком потоке у меня находится и куда я обращаюсь? не хочу. геморно. "тут так напиши, а тут так"...
(кстати, спасибо за инфу - не залазил в данную часть ассистанта со времен 4.3 - тогда этого типа соединения не было)))

А почему не через sender()?
мне не надо обращаться к объекту-источнику. Мне надо вернуть ему результат.

Далее - попытка использовать sender() требует от меня знать класс объекта который меня вызывает. и даже не говоря о том, что поэтому sender() "нарушает" "сигнал-слотовую парадигму"(?)(см замечания троллей в ассистанте), - он работает только в рамках соединений типа QT::QuotedConnection - т.е. в рамках одного потока.
(или в новых версиях QT уже это поменяли??))))
« Последнее редактирование: Октябрь 14, 2010, 01:03 от Denjs » Записан
spectre71
Гость
« Ответ #8 : Октябрь 14, 2010, 01:44 »

1) Например Qt::DirectConnection, или выставить флаг, или послать обратный сигнал, или ...
2) Например Qt::DirectConnection, или выставить флаг, или послать обратный сигнал, или ...
3) поскольку ты где-то делаешь прямое подключение, легко сделать там и обратное подключение - самое примтивное. Или ...
4) Интерфейсы, менеджеры интерфейсов и прочая паттерная хрень Улыбающийся Или ...
Qt::DirectConnection при межпотоковом взаимодействии всегда(!) становится Qt::QuotedConnection;

выставлять флаг? где? и куда смотреть? связанные объекты даже ссылок не имеют друг на друга....;

обратное подключение?! и что? отправив сигнал-запрос, я должен ловить результат в другом слоте, а в итоге - "одну простую функцию" разносить на несколько слотов и фактически - "делать громоздкую реализацию машины состояний" вместо одного простого линейного алгоритма который должен запросить несколько фнешних параметров?? не-не-нееее)

"Интерфейсы, менеджеры интерфейсов и прочая паттерная хрень"? дада) именно хрень. тем более в условиях когда сигнатура функции фактически и является описанием мини-интерфейса.


Я не зря написал везде или ... Вариантов много... Для разных ситуаций - свой.
А про "хрень", интерфейсы в большинстве случаев более оптимальное решение для твоего пункта (4).
Теперь про это: "обратное подключение?! и что? отправив сигнал-запрос, я должен ловить результат в другом слоте"
Ты хочешь всегда! - отправил запрос - ждем результата? А как же асинхронная работа потоков. Используй тогда  DirectConnection.
Записан
SASA
Гость
« Ответ #9 : Октябрь 14, 2010, 10:21 »

  • Как проверить что твой сигнал получен и обработан?
  • Как дождаться момента когда твой сигнал обработан?
  • Как получить значения, рассчитанные в приемном слоте, к которому подключен твой сигнал? (ведь ты не знаешь куда он даже подключен?)
  • Как избавиться от "лишнего груза знаний" о том, кто должен рассчитывать нужные тебе параметры?
1. Это противоречит идеи сигнал/слот.
2. Уже есть - Qt::ConnectionType.
3. Это противоречит идеи сигнал/слот. (но сам грешен, получаю  Плачущий)
4. Шаблон - стратегия.

Можно продумать идею "Единичный сигнал". Т.е. сигнал соединяется всегда с одним слотом. Т.е. упростить идею до непрямого вызова функции. Это уже есть QMetaObject::invokeMethod. Но нужно знать у кого вызывать, что не всегда удобно.

З.Ы. Можно прикрепить  архив со всем проектом, что изучать код в любимой IDE.
« Последнее редактирование: Октябрь 14, 2010, 10:30 от SASA » Записан
Denjs
Гость
« Ответ #10 : Октябрь 14, 2010, 11:51 »

Используя термины SASA, полагаю можно сказать, что :

... назначение класса t_returnSyncObject - обеспечить механизм непрямого синхронного "потоко-безопасного" вызова функций, через сигнал-слотовый механизм, с возвратом рассчитываемого значения.

По сравнению с QMetaObject::invokeMethod, данный механизм лучше тем, что объекту-пользователю нет необходимости знать кто именно обслуживает его обращение.

По сравнению различными типами подключения (Qt::DirectConnection/Qt::BlockingQueuedConnection/QT::QuotedConnection) - данный механизм обеспечивает "псевдо-синхронное взаимодейтсвие" не требующее специального типа подключения в зависимости от нахождения объектов в одном или разных потоках.

и расскажите подробнеее - почему вы считаете что желание "проверить что твой сигнал получен и обработан?" - это "противоречит идеи сигнал/слот."? разве, скажем TCP противоречит UDP? )))))
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #11 : Октябрь 14, 2010, 12:55 »

PS: В качестве бонуса - "кроссплатформенный Sleep" - static void SleepMS_qt( unsigned int time)

Как вариант могу предложить следующий хак:

Код
C++ (Qt)
#include <QThread>
 
class QThreadHack : public QThread
{
public:
   using QThread::sleep;
   using QThread::msleep;
   using QThread::usleep;
};
 
...
// using
QThreadHack::msleep(5000);
 

Кстати для меня загадка, зачем тролли сделали эти методы protected  Непонимающий
Записан

Гугль в помощь
Denjs
Гость
« Ответ #12 : Октябрь 14, 2010, 16:58 »

PS: В качестве бонуса - "кроссплатформенный Sleep" - static void SleepMS_qt( unsigned int time)
Как вариант могу предложить следующий хак:
гы! да это же реализация (анти)паттерна Паблик Морозов!
« Последнее редактирование: Октябрь 14, 2010, 17:06 от Denjs » Записан
BRE
Гость
« Ответ #13 : Октябрь 14, 2010, 17:19 »

Кстати для меня загадка, зачем тролли сделали эти методы protected  Непонимающий
Потому что нет никакой необходимости усыплять главный поток.
Записан
SASA
Гость
« Ответ #14 : Октябрь 14, 2010, 17:20 »

и расскажите подробнеее - почему вы считаете что желание "проверить что твой сигнал получен и обработан?" - это "противоречит идеи сигнал/слот."? разве, скажем TCP противоречит UDP? )))))

Сигнал - это способ сообщить всем, что с объектом что-то случилось. Параметры - подробности случившегося. Этот сигнал могут принять любое (включая ноль) количество заинтересованных.

Например. Нажали кнопку  "Выход". Кнопки нет дела, до того, что она делает. Но она говорит "Меня нажали". Приложение ловит сигналы от кнопки и отрабатывает реакцию - закрывается.

З.Ы. Это в идеале. Но злостные хукеры, типа нас с Вами, используют этот механизм в своих низменных целях Смеющийся

З.З.Ы. Пошел учить паттерны.
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.084 секунд. Запросов: 23.