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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Передача данных между QThread без сигнал-слотов. Безопасно ли так делать?  (Прочитано 18708 раз)
IGHOR
Крякер
****
Offline Offline

Сообщений: 390



Просмотр профиля WWW
« : Июль 20, 2012, 01:48 »

Есть главный класс наследник QObject в котором есть слот someSlot() и сигнал initSomeSlot() который тоже в этом объекте.
Есть множество запущенных QThread наследников и у каждого из них есть указатель на главный QObject.
Безопасно (thread safe) ли вызывать внутри QThread по указателю метод из основного QObject который инициализирует сигнал initSomeSlot()?
Делается это чтобы не тормозить поток в главном QObject и чтобы не делать кучу connect для каждого QThread.
« Последнее редактирование: Июль 21, 2012, 11:23 от IGHOR » Записан
Bepec
Гость
« Ответ #1 : Июль 20, 2012, 06:48 »

Т.е. у тебя вызывается слот основного потока из других потоков?

А что этот слот делает?

Записан
sidsukana
Гость
« Ответ #2 : Июль 20, 2012, 07:28 »

А мутексы уже не в моде? Ну или на крайняк "метод основного объекта" сделать слотом и соединить его с сигналом из потока по связи BlockedConnection.
Записан
Bepec
Гость
« Ответ #3 : Июль 20, 2012, 07:31 »

Блокед коннекшен убьёт гуи.

Тут же какая то кривая реализация проскакивает Веселый

PS совет - используйте сигнал-слоты. Не мешайте чёрное с горячим, жёсткое с дешёвым, вызов сигнал слота прямым вызовом и коннект сигнала Веселый
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Июль 20, 2012, 11:51 »

Безопасно (thread safe) ли вызывать внутри QThread по указателю метод из основного QObject который инициализирует сигнал initSomeSlot()?
То "инициализирует" - большая мутность. Наверное эмиттит, испускает сигнал. Если код слота thread-safe то да. Если нет - использовать QueuedConnection (при этом однако слот сработает позже что может не устроить)
Записан
IGHOR
Крякер
****
Offline Offline

Сообщений: 390



Просмотр профиля WWW
« Ответ #5 : Июль 20, 2012, 22:50 »

Попробую объяснить подробнее..
Есть несколько тредов, 8 например.
И динамически создаются объекты наследники QObject при этом каждый перемещается в один из тредов и никогда не удаляется.
Каждый объект должен посылать данные QByteArray в "главный" поток.
Хочу избежать создания множества пар сигнал слотов при добавлении нового объекта в тред.
Это не критично но упростит понимание и является оптимизацией (или нет?).
Значит:
1) у каждого объекта есть указатель на "главный"
2) в "главном" объекте есть метод (не слот) в котором инициируется сигнал соединенный с слотом "главного" объекта.
Сигнал инициируется в треде и так думаю в результате должен запустится слот в "главном" объекте, будет ли это threadsafe?
Записан
kostya2vntu
Гость
« Ответ #6 : Июль 21, 2012, 01:38 »

Код в студию  Подмигивающий
Записан
IGHOR
Крякер
****
Offline Offline

Сообщений: 390



Просмотр профиля WWW
« Ответ #7 : Июль 21, 2012, 03:31 »

Подумал и обобщил вопрос.
Есть два треда ThreadA и ThreadB. У первого есть указатель на второй.
Надо передавать данные из А в В без сигналов слотов между ThreadA и ThreadB так чтобы это было ThreadSafe и не тормозило треды.
И да, код слота slotSendData есть ThreadSafe и он обрабатывает данные и может отправить их в другой тред.
Прилагаю файлы с примером как я это сделал.

Есть множество тредов (или объектов в разных тредах) между которыми надо передавать данные в QByteArray.
Я сделал так что каждый объект сбрасывает данные в главный тред и там определяется куда его отправить дальше (в другой тред).
Думаю нельзя соединить много объектов сигналами во всех комбинациях поэтому так делаю.


Алгоритм кратко:
1) ThreadA по указателю вызывает функцию functionSendData из ThreadB
2) в функции functionSendData вызывается сигнал signalSendData который соединен со слотом в этом же ThreadB
3) обрабатывается слот slotSendData изпод ThreadB
Выходит ThreadA по сигналу запускает слот в ThreadB и при этом нет явного соединения ThreadA<=>ThreadB и любой тред может обратится к другому без предварительного connect к нему.
Скажите оптимально ли это реализовано?

main.cpp
Код:
#include <QtGui/QApplication>
#include "threada.h"
#include <QDebug>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug()<<"START ============================";

qDebug()<<"Main Thread ID: "<<a.thread()->currentThreadId();
ThreadB threadB;
ThreadA threadA(&threadB);

return a.exec();
}

threada.h
Код:
#ifndef THREADA_H
#define THREADA_H

#include <QThread>
#include "threadb.h"

class ThreadA : public QThread
{
Q_OBJECT

public:
ThreadA(ThreadB*);
void run();

private:
ThreadB *threadB;
};

#endif // THREADA_H

threada.cpp
Код:
#include "threada.h"
#include <QDebug>

ThreadA::ThreadA(ThreadB *thB)
: QThread()
{
threadB=thB;
moveToThread(this);
start();
}

void ThreadA::run()
{
QByteArray dataToSend="HELLO WORLD!";
qDebug()<<"functionSendData("+dataToSend+") From "<<currentThreadId();
threadB->functionSendData(dataToSend);
//exec();
}

threadb.h
Код:
#ifndef THREADB_H
#define THREADB_H

#include <QThread>

class ThreadB : public QThread
{
Q_OBJECT

public:
ThreadB();

void functionSendData(QByteArray);

private slots:
void slotSendData(QByteArray);
signals:
void signalSendData(QByteArray);
};

#endif // THREADB_H

threadb.cpp
Код:
#include "threadb.h"
#include <QDebug>
#include <QCoreApplication>

ThreadB::ThreadB()
: QThread()
{
connect(this,SIGNAL(signalSendData(QByteArray)),this,SLOT(slotSendData(QByteArray)));
moveToThread(this);
start();
}

void ThreadB::slotSendData(QByteArray data)
{
//чтото делать с данными
qDebug()<<"slotSendData("+data+") From "<<currentThreadId();
qDebug()<<"END ============================";
QCoreApplication::quit();
}

void ThreadB::functionSendData(QByteArray data)
{
qDebug()<<"signalSendData("+data+") From "<<currentThreadId();
emit signalSendData(data);
}

Вывод Debug
Код:
Main Thread ID: 0x1af4 
functionSendData(HELLO WORLD!) From 0x1f6c
signalSendData(HELLO WORLD!) From 0x1f6c
slotSendData(HELLO WORLD!) From 0x1e34
« Последнее редактирование: Июль 21, 2012, 03:45 от IGHOR » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Июль 21, 2012, 10:44 »

Это может быть thread-safe или нет в зависимости от содержания ThreadB::functionSendData. Вот пример когда нет
Код
C++ (Qt)
void ThreadB::functionSendData(QByteArray data)
{
mData += data;
}
 
При вызове этого метода напрямую он выполняется в той же нитке что и вызывающий. Если 2 или более нитки одновременно выполняют код выше - рухнет т.к. += контейнера не thread-safe. Если же защитить такие места мутексами - все норм 
Записан
IGHOR
Крякер
****
Offline Offline

Сообщений: 390



Просмотр профиля WWW
« Ответ #9 : Июль 21, 2012, 11:06 »

Это может быть thread-safe или нет в зависимости от содержания ThreadB::functionSendData. Вот пример когда нет

Функция functionSendData создана только для того чтобы запустить сигнал с данными.
Там будет только одна строчка как в примере.
А обработка данных только в slotSendData.
Это разве не ThreadSafe?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Июль 21, 2012, 11:19 »

Функция functionSendData создана только для того чтобы запустить сигнал с данными.
Там будет только одна строчка как в примере.
А обработка данных только в slotSendData.
Это разве не ThreadSafe?
В Вашем коде этот сигнал вызывается через EventLoop (сработает QueuedConnection), поэтому эффект/оптимизация те же самые как если бы ThreadA испустила сигнал. А если использовать DirectConnection, то с thread-safe все то же
Записан
IGHOR
Крякер
****
Offline Offline

Сообщений: 390



Просмотр профиля WWW
« Ответ #11 : Июль 21, 2012, 11:38 »

эффект/оптимизация те же самые как если бы ThreadA испустила сигнал.
По поводу оптимизации тоже думаю что нет разницы.
В итоге получилась конструкция (new QThread)->sendData(..) которая вызывает сигнал по функции из другого треда.
И у каждого QThread есть указатель на главный QThread, данные передаются в последний этим методом а потом определяется в который QThread отправить их дальше, и тоже по указателю так делается.

В результате сигнал со слотом соединены только внутри каждого объекта самому к себе и нет надобности соединять сигнал со слотом всех остальных тредов.

А как бы вы реализовали передачу данных между 1000 штук QThread (или просто QObject)?
Разве удобно создавать между всеми ими связь?
« Последнее редактирование: Июль 21, 2012, 11:40 от IGHOR » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Июль 21, 2012, 11:50 »

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

А как бы вы реализовали передачу данных между 1000 штук QThread (или просто QObject)?
Разве удобно создавать между всеми ими связь?
Это столь же удобно как и хранить 100 штук указателей на "основную" нитку. Но Вы же эти 100 указателей как-то записали  Улыбающийся
Записан
Bepec
Гость
« Ответ #13 : Июль 21, 2012, 12:49 »

Как говорил один человек - если в программе больше 20 потоков - это плохая программа Веселый
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Июль 21, 2012, 13:04 »

Как говорил один человек - если в программе больше 20 потоков - это плохая программа Веселый
То когда было. А сейчас меньше 8 ядер - бедновато, а 16-ю никого не удивишь
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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