Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: mad от Июнь 05, 2016, 20:49



Название: Вопрос по примеру QThread из офф. документации
Отправлено: mad от Июнь 05, 2016, 20:49
В офф.документации есть такой пример (http://doc.qt.io/qt-5/qthread.html#details):

Код:
class Worker : public QObject
{
    Q_OBJECT

public slots:
    void doWork(const QString &parameter) {
        QString result;
        /* ... here is the expensive or blocking operation ... */
        emit resultReady(result);
    }

signals:
    void resultReady(const QString &result);
};

class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;
public:
    Controller() {
        Worker *worker = new Worker;
        worker->moveToThread(&workerThread);
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
        connect(this, &Controller::operate, worker, &Worker::doWork);
        connect(worker, &Worker::resultReady, this, &Controller::handleResults);
        workerThread.start();
    }
    ~Controller() {
        workerThread.quit();
        workerThread.wait();
    }
public slots:
    void handleResults(const QString &);
signals:
    void operate(const QString &);
};

В принципе почти все ясно, хочу свои догадки подтвердить. Плохо, что в примере не показывается, как именно по их задумке должен использоваться класс Controller в реальной практике. Смущает сигнал operate. Т.е. для запуска операции в другом потоке нужно выработать сигнал внутри себя?
Это такой красивый лайфхак? Чтобы уйти от использования не красивого invokeMethod? Я вообще правильно понимаю, что connect и invokeMethod по сути делают одно и то же? Как этот класс подразумевается использовать в реальной ситуации? Добавить мембер функцию, которая внутри класса испускает сигнал

Код:
void Controller::doOperate(const QString & str)
{
...
emit operate(str);
}


или дергать напрямую сигнал инстанса этого класса извне:
Код:
int main(int argc, char *argv[])
{
...

Controller * cntrl = new Controller ();
emit cntrl->operate ("somestring");

}

Второй вариант как-то хитро выглядит, хотя и легитимный, судя по изучению ресурса stackoverflow. Насколько хорошо вырабатывать сигнал другого объекта?
Как бы вы предложили использовать этот класс, какая лучшая практика для этого существует?


ЗЫ Да, я понимаю, что emit это пустой макрос. Но в рамках философии Qt нормально ли "чужой" сигнал дергать?
Таки для чего это так сделано в примере через сигнал, если сигнал operate убрать и не использовать сигнал started у класса QThread, то кроме как через methodInvoke уже не выкрутиться, чтобы doWork выполнился именно в другом потоке, правильно понимаю? Всего лишь хочу понять логику писавшего этот пример :)









Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: _OLEGator_ от Июнь 05, 2016, 21:02
Это такой красивый лайфхак? Чтобы уйти от использования не красивого invokeMethod? Я вообще правильно понимаю, что connect и invokeMethod по сути делают одно и то же?

Читайте внимательно документацию. Connect также разруливает вызов слота между потоками (в контексте какого потока будет вызван слот).


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: mad от Июнь 05, 2016, 21:32
Ну так это я понимаю, просто в голове логически не укладывается вызывать сигнал другого объекта. Я в Qt новичок и начал сразу с 5.6 версии.
Судя по всему раньше чужие сигналы вне класса нельзя было вызывать(Qt4), а теперь это работает
Код:
emit cntrl->operate("somestr");
т.к.
Код:
#     define signals public

для чего это сделано и хорошо ли это использовать?)


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: _OLEGator_ от Июнь 05, 2016, 22:46
Не знаю для чего это сделано, но выглядит плохо, это так называемый запах - вроде все работает, никто не запрещает так делать - но выглядит костыльно.
Я бы не стал так делать - это косвенный показатель плохой архитектуры. Объект сам должен информировать других сигналами, странно выглядит когда внешний объект их начинает испускать  :)


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Bepec от Июнь 05, 2016, 22:48
Чужой сигнал дёргать плохо. Ибо сигналы могут вызывать реакцию, которая приведёт к падению программы или некорректной работе :) Вот если вы точно знаете что сигнал можно дёргать - тогда пожалуйста :D

Свой сигнал дёргать есть хорошо :) Можно дёргать нежно, можно дёргать жёстко, кому как нравится.

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

Connect соединяет сигналы и слоты. InvokeMethon вызывает сигналы и слоты.  Тёплое с мягким не путайте :)

PS в идеале да, должна быть паблик функция, которая заставляет controller вызывать свой сигнал.

PPS а сделаны пабликами сигналы скорее всего для улучшения отладки. Чтобы не приходилось портить тестируемый класс дебажными вставками :)


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: mad от Июнь 06, 2016, 00:05
Спасибо за ответы, учту :)
Цитировать
Connect соединяет сигналы и слоты. InvokeMethon вызывает сигналы и слоты.  Тёплое с мягким не путайте

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

Еще мысли....по примеру.
А как же тогда скрыть сигнал (сделать приватным), если хочется использовать его для внутренних целей класса. А так получается торчит он наружу, соблазняет с ним делать всякое нехорошее)) Хотя чисто внутренний сигнал - кажется бредовой идеей. Конечно, его (сигнал operate) можно соединить с другим сигналом где-либо в программе, и это будет красивее с точки зрения Qt, но это ведь не будет чем-то более лучшим, чем emit cntrl->operate("somestr"); Будет emit anothersignal("somestr").

Хм, есть некий хак QPrivateSignal

очень полезные статьи:
https://habrahabr.ru/post/214379/
https://habrahabr.ru/post/215181/


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Igors от Июнь 06, 2016, 07:41
Но в рамках философии Qt нормально ли "чужой" сигнал дергать?
Да, нормально. В данном случае Controller не располагает никаким интеллектом чтобы решать когда надо начинать вычисления, он только может их начать. Значит тот кто обеспечивает параметры сигнала operate и должен "эмиттить".

Эх, получается я криво спросил, имелось ввиду, что выработка сигнала с последующим вызовом подключенного слота это тоже самое, что и вызов через invokeMethod, т.е. их механизм работы в недрах Qt одинаков?
Да, только нет никакой "выработки", после того как отработал moc, сигнал - такой же метод как если бы Вы его написали сами.

А как же тогда скрыть сигнал (сделать приватным), если хочется использовать его для внутренних целей класса. А так получается торчит он наружу, соблазняет с ним делать всякое нехорошее)) Хотя чисто внутренний сигнал - кажется бредовой идеей. Конечно, его (сигнал operate) можно соединить с другим сигналом где-либо в программе, и это будет красивее с точки зрения Qt, но это ведь не будет чем-то более лучшим, чем emit cntrl->operate("somestr"); Будет emit anothersignal("somestr").
Слишком много концептуальности/философии  :)  Да, иногда хотелось бы подчеркнуть что сигнал чисто "местный", ну это дело невеликое, достаточно просто примечаний. А борцы за идейную чистоту могут напр ограничить доступ к самому классу Controller  :)


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: ssoft от Июнь 06, 2016, 08:25
Но в рамках философии Qt нормально ли "чужой" сигнал дергать?

В рамках любой философии дергать "чужой" сигнал не нормально.

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

В случае внешнего вызова сигнала причинно-следственная связь нарушается, так как сам объект фактически ни как не реагировал на какое-либо событие, и систему можно привести в несогласованное состояние. Например, вызывая сигнал у QSlider valueCanged(int) все взаимосвязанные объекты изменят свое состояние, а сам QSlider нет.


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Igors от Июнь 06, 2016, 09:26
Подразумевается, что через механизм сигнал-слот взаимодействия обеспечивается некоторая согласованность данных в программном обеспечении. То есть изменение состояния одного объекта может быть сигнализировано другим с помощью сигнала, на основании которого связанные с ним объекты могут поменять свое состояние им т.п. Таким образом взаимосвязанные объекты будут находится в согласованном состоянии - в единой модели причинно следственных связей.
Эти (разумные) соображения столь же справедливы для любого метода класса (должен ли он быть public/private/protected). Считать сигнал "чем-то особенным" нет оснований.

В случае внешнего вызова сигнала причинно-следственная связь нарушается, так как сам объект фактически ни как не реагировал на какое-либо событие,
А на что может среагировать Controller в примере? У него же ничего нет чтобы он мог что-то решать. И почему сигнал рассматривается только как "реакция" (на что-то) ? Это может быть по сути просто (piblic) "командой"


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Bepec от Июнь 06, 2016, 10:52
Не спорьте, вы оба правы.
В случае с примером у нас нет чужого класса. У нас есть свой класс с, пусть нелепой, но допустимой механикой.
Но вот когда класса не знаешь, лучше так не делать :)


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: ssoft от Июнь 06, 2016, 12:10
Эти (разумные) соображения столь же справедливы для любого метода класса (должен ли он быть public/private/protected).

Я согласен с этим. Просто для сигналов в Qt не оставлено другого выбора кроме public. И получается, что по такому принципу можно дернуть любой сигнал, а это уже косяк архитектуры.

Считать сигнал "чем-то особенным" нет оснований.

Понятие сигнала  достаточно абстрактное и в общем случае может быть реализовано не только в виде методов класса, как в Qt. Но это большая тема для другого обсуждения, если хотите  :).


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: ViTech от Июнь 06, 2016, 13:42
Не так давно была тема (http://www.prog.org.ru/topic_29431_0.html) про области видимости для сигналов.

Если коротко, то объект должен предоставлять возможность подключаться к своим сигналам и отключаться от них другим объектам, а возможность "вызывать" их должен иметь только он сам. То, что в Qt сигналы объявлены в публичной секции, чтобы можно было подключаться и отключаться от них - это нормально, а то, что кто угодно может "вызвать" эти сигналы - ненормально, и это косяк архитектуры.

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

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

Как бы вы предложили использовать этот класс, какая лучшая практика для этого существует?
...
Таки для чего это так сделано в примере через сигнал, если сигнал operate убрать и не использовать сигнал started у класса QThread, то кроме как через methodInvoke уже не выкрутиться, чтобы doWork выполнился именно в другом потоке, правильно понимаю? Всего лишь хочу понять логику писавшего этот пример :)

Мне тоже логика этого класса не очень понятна :). Разве что демонстрация, что "можно так вот делать". Может Controller будет базовым классом для других контроллеров, но всё равно это криво. Должен быть публичный метод, который будет вызывать этот сигнал operate.


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Igors от Июнь 06, 2016, 14:11
Мне тоже логика этого класса не очень понятна :). Разве что демонстрация, что "можно так вот делать". Может Controller будет базовым классом для других контроллеров, но всё равно это криво. Должен быть публичный метод, который будет вызывать этот сигнал operate.
А по-моему очень неплохой пример. Обычно никакого контроллера-то и нет, (и воркера тоже), наследуются от QThread - и все дела. А здесь показано как сделать грамотно, все разложено  по полочкам. Воркер делает содержательную работу не заботясь в какой он нитке. Контроллер связывает его с QThread, которую умеет запускать/завершать, сигналами ему можно скармливать задачи и получать рез-т. Чего еще надо? Посвящать контроллер в подробности уже какой-то конкретной задачи - явно лишнее


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Bepec от Июнь 06, 2016, 17:33
Логика этого класса в том, что в сигнал слотовой системе можно подключать СИГНАЛЫ к СЛОТАМ и(sic!) СИГНАЛАМ.

По сути сделан connect сигнала своего класса к сигналу operate - и вот уже полноценный контроллер с воркерами пошёл в работу :)

PS по сути он рассчитан на работу с "классом". А вызов функции invoke - это уже издержки :D


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: sergek от Июнь 07, 2016, 20:26
Обычно никакого контроллера-то и нет, (и воркера тоже), наследуются от QThread - и все дела. А здесь показано как сделать грамотно, все разложено  по полочкам. Воркер делает содержательную работу не заботясь в какой он нитке.
Это, если вся функциональность (с созданием объектов) реализована в run(). Если вам нужно вызвать слот в объекте, созданном в другом потоке, тогда подобный контроллер - очевидное (наверное, не единственное) решение. Для этого и служит operate - в каком-нибудь методе контроллера испускается сигнал, при этом в другом потоке выполняется слот. Можно таким образом передавать данные из потока в поток.
И наоборот, из этого потока данные (или управление) передаются через сигнал resultReady в главный поток.


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Авварон от Июнь 08, 2016, 14:33
Спасибо за ответы, учту :)
А как же тогда скрыть сигнал (сделать приватным), если хочется использовать его для внутренних целей класса. А так получается торчит он наружу, соблазняет с ним делать всякое нехорошее))

Раньше (в Qt 4) сигналы были protected. В 5ке добавили новый синтаксис (https://wiki.qt.io/New_Signal_Slot_Syntax/ru) коннекта. Чтобы он работал, пришлось сделать сигналы public (иначе, компилятор выдаёт ошибку несовместимости сигнатур). С++ говно, да.


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Bepec от Июнь 08, 2016, 16:15
И в 4 и в 5 можно было вызывать сигналы других классов, соединять их и так далее и тому подобное.
В каком они разделе находились - public или private неважно, ведь соединение создавал сам объект :D


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Авварон от Июнь 08, 2016, 16:24
И в 4 можно было вызывать сигналы других классов

Напрямую - нельзя. #define signals protected


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Bepec от Июнь 08, 2016, 17:30
Это уже частность. создай свой сигнал и свяжи с любым приватным. Вуаля. Сделай invokeMethod и вызывай любой сигнал. Тадам. Но вот прямо вызвать - да, нельзя :D Но это уже фикция.


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Авварон от Июнь 08, 2016, 17:55
Это уже частность. создай свой сигнал и свяжи с любым приватным. Вуаля. Сделай invokeMethod и вызывай любой сигнал. Тадам. Но вот прямо вызвать - да, нельзя :D Но это уже фикция.

#define private public
и можно вызывать всё что угодно, да...


Название: Re: Вопрос по примеру QThread из офф. документации
Отправлено: Bepec от Июнь 08, 2016, 21:50
То, что вы предложили - хак. То, что предложил я - публичная часть работы сигнал слотовой системы. Ну да и ладно :)