LINUX.ORG.RU

Передача данных в Qt5 через сигнал-слоты между тредами и в одном треде с минимальным оверхедом

 , ,


1

2

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

Но здесь проблема. Создадим объект в стеке и пошлем его по ссылке через сигнал-слоты:

void mysignal(const Foo &);

//...
Foo foo;
emit mysignal(foo);

При передаче в пределах одного треда всё будет ок, так как объект разрушиться после вызова сигнала и привязанных слотов.

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

Вопросы: Как элегантно этого избежать?

Как сделать, чтобы передача данных была одинаково эффективная и внутри одного потока и между потоками?

Лично я вижу выход в использовании:

  • Указатели и создание в куче. Плюсы — никаких накладных расходов. Минусы — нужно следить за жизнью объекта.
  • Смарт-поинтеры. Плюсы — не нужно помнить про необходимость разрушения объекта, ну и минимальные накладные расходы. Минусы — код не красивый.
  • Использование Implicit Sharing и передача данных по значению. Код красивый, но не всегда можно переделать классы.

В общем, мне пока больше всего нравятся смарт-поинтеры.

Можно ли как-то выкрутится с помощью rvalue?

Минусы — код не красивый.

typedef std::shared_ptr <MyClass> pMyClass;

nanoolinux ★★★★
()

Подключение между потоками бывает Auto, Queued и BlockingQueued и все они должны работать согласно документации. Сохранение временной копии обьекта также возможна с помощью, к примеру, std::decay. Желателен минимальный пример разименования потенянной ссылки, если подтвердится — на багтрекер Qt.

Dendy ★★★★★
()

Поправьте, если я ошибаюсь.

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

Нормализация это несколько другое. Её цель делать сигналы и слоты совместимыми.

Adonai ★★★
()
Последнее исправление: Adonai (всего исправлений: 2)

Можно ли как-то выкрутится с помощью rvalue?

Rvalue reference, быть может? Они нужны в основном для конструктора и оператора переноса. Здесь объект не прибывает _всегда_ на метод назначения, они неприменимы.

Пробовал emit mysignal(Foo()); ?

Adonai ★★★
()
Последнее исправление: Adonai (всего исправлений: 1)
Ответ на: комментарий от Dendy

Заранее нельзя сказать, какой конкретно тип соединения будет, так как объект может использоваться и в отдельном потоке, и в том же. Поэтому нужно универсальное потокобезопасное решение.

Желателен минимальный пример разименования потенянной ссылки, если подтвердится — на багтрекер Qt.

А разве это и так не очевидно? Мы передаем ссылку на объект, который разрушается в другом потоке. Контролировать время жизни объекта можно только при Direct и BlockingQueued. В случае с Queued получаем типичное состояние гонки.

std::decay

Почитаю.

Chaser_Andrey ★★★★★
() автор топика
Ответ на: комментарий от Chaser_Andrey

И там, и там такое поведение.

Новый синтаксис хорош для проверки типов во время компиляции и для лямбд.

Adonai ★★★
()
Ответ на: комментарий от Adonai

Если в разных - тогда сериализация и копирование.

Без сериализации, всё-таки, не меж процессами объект передаётся.

Begemoth ★★★★★
()

1. Для передачи объектов через сигналы-слоты между потоками для их типа должен быть специализирован QMetaType (man Q_DECLARE_METATYPE). При этом будет минимум одно копирование аргументов сигнала. Вероятно можно этого избежать специализировав QMetaType вручную.

2. Для класса Foo конструктор копирования определён корректно?

Begemoth ★★★★★
()
Последнее исправление: Begemoth (всего исправлений: 1)
Ответ на: комментарий от Begemoth

Да, верно, прошу прощения за дезинформацию

Adonai ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.