Подтвердить что ты не робот

Qt сигнализирует по потокам, один - поток GUI?

Что означает перемещение объекта из одного потока в другой в Qt с помощью moveToThread? Кажется, что все работает даже до использования moveToThread, который перемещает объект из одного потока (поток GUI) в другой поток (работает) и Qt: connect вызывает соответствующий слот на объекте.

Есть ли разница в том, где живет объект, поток GUI или рабочий поток?

EDIT: Я сделал небольшую программу, но я не понимаю, как работает QThread вместе с функцией Signal и slot, я был бы признателен, если бы вы могли объяснить, что такое использование moveToThread с примером

#include <QtGui/QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QString>
#include "mythread.h"
//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    QHBoxLayout * pH = new QHBoxLayout(&w);
    QPushButton * pushButton = new QPushButton("asdad");
    QLineEdit * lineEdit = new QLineEdit("AAA");
    pH->addWidget(pushButton);
    pH->addWidget(lineEdit);
    w.setLayout(pH);
    w.show();
    MyThread thread;
    qDebug("Thread id %d",(int)QThread::currentThreadId());
    QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(callRun())) ;
    QObject::connect(&thread,SIGNAL(signalGUI(QString)),lineEdit,SLOT(setText(QString)));
    return a.exec();
}

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QMutex>

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread();
public slots:
    void callRun();
    void run();
 signals:
    void signalGUI(QString);
private:
    QMutex mutex;

};

#endif // MYTHREAD_H


#include "mythread.h"
#include <QDebug>
#include <QString>
#include <QMutexLocker>

MyThread::MyThread()
{
}
 void MyThread::callRun()
 {

     qDebug("in thread");
    if(!isRunning())
     {
        this->start(LowestPriority);
        exec();
    }
    else
    {
        run();
    }

 }
 void MyThread::run()
 {
     QMutexLocker fn_scope(&mutex);
     static int a = 0;
    ++a;
     qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
     this->sleep(3);
     static QString number;
     QString temp;
     number += temp.setNum(a);
     emit signalGUI(number);
 }
4b9b3361

Ответ 1

Взгляните на Сигналы и слоты в потоках. Если вы всегда используете сигналы и слоты для связи с рабочим потоком, Qt обрабатывает moveToThread для вас, если это необходимо, и вы использовали правильное соединение.

Изменить: я бы предположил, что автор статьи видел свою проблему, так как он вызывал начало в конструкторе до того, как поток был фактически создан. Другими словами, не доверяйте стороннему коду вслепую.

Изменить: в ответ на ваш комментарий просмотрите пример Mandelbrot в заголовке MandelbrotWidget Class Implementation:

При подключении в очереди Qt должен хранить копию аргументов, переданных сигналу, чтобы впоследствии передать их в слот. Qt знает, как взять копию многих типов С++ и Qt, но QImage не является одним из них. Поэтому мы должны вызвать функцию шаблона qRegisterMetaType(), прежде чем мы сможем использовать QImage в качестве параметра в связанных в очереди соединениях.

Я считаю, что это немного устарело, вот допустимые мета-типы. Поскольку сигналы и слоты в потоках используют очереди в очереди, вам не нужно делать вызовы moveToThread в большинстве случаев.

Изменить: Я попытаюсь объяснить вещи аналогичным примером:

mythread.h:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QMutex>

class MyThread : public QThread
{
   Q_OBJECT

protected:
   virtual void run();

signals:
   void signalGUI(QString);
};

#endif // MYTHREAD_H

mythread.cpp:

#include "mythread.h"
#include <QString>

void MyThread::run()
{
   qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
   static int run = 0;
   QString temp = QString("Run: %1").arg(run++);
   qDebug("String address inside run %p", &temp);
   emit signalGUI(temp);
}

mylineedit.h

#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H

#include <QLineEdit>

class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
    explicit MyLineEdit(QWidget *parent = 0);

public slots:
    void setText(const QString &string);

};

#endif // MYLINEEDIT_H

mylineedit.cpp

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

MyLineEdit::MyLineEdit(QWidget *parent) :
    QLineEdit(parent)
{
}

void MyLineEdit::setText(const QString &string)
{
   qDebug("Thread id inside setText %d",(int)QThread::currentThreadId());
   qDebug("String address inside setText %p\n", &string);
   QLineEdit::setText(string);
}

main.cpp:

#include <QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include "mythread.h"
#include "mylineedit.h"

//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   QWidget w;
   QHBoxLayout * pH = new QHBoxLayout(&w);
   QPushButton * pushButton = new QPushButton("Run Thread", &w);
   MyLineEdit * lineEdit = new MyLineEdit(&w);

   pH->addWidget(pushButton);
   pH->addWidget(lineEdit);
   w.show();

   MyThread thread;
   qDebug("Thread id %d",(int)QThread::currentThreadId());
   QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(start())) ;
   QObject::connect(&thread,SIGNAL(signalGUI(const QString&)),lineEdit,SLOT(setText(const QString&)));
   return a.exec();
}

Пример вывода после нажатия кнопки:

Thread id 1088110320
Thread id inside run 1093176208
String address inside run 0x41288350
Thread id inside setText 1088110320
String address inside setText 0x974af58

Как вы можете видеть, поток выполнения отличается от потока основного GUI. Кроме того, несмотря на то, что вы передаете ссылку const на QString, поскольку она пересекает границы потоков, она копирует ее. Я сильно рекомендую вам читать Threads и QObject.

Ответ 2

  1. Метод QThread::start() создает поток и вызывает реализацию run(). Если вы хотите обрабатывать события или полученные сигналы в потоке, вы должны вызвать QThread::exec() внутри вашей реализации run(). Вы никогда не должны вызывать run() явно, и вы никогда не должны называть exec() вне run().

  2. Пользовательский поток имеет значение только тогда, когда слот подключен к сигналу с типом соединения, отличным от Qt::DirectConnection. Тогда Qt гарантирует, что слот будет работать в потоке владельца, но для этого поток владельца должен запускать цикл событий с помощью QThread::exec(). В этом случае вызов myObj.moveToThread(myThread) обеспечит выполнение myObj слотов в потоке myThread.

  3. Объект потока принадлежит потоку, где он был создан, а не потоку, которым он управляет (и где будет запускаться метод запуска). Поэтому, когда вы подключаете сигнал к слоту объекта потока, этот слот будет запускаться в потоке, где был создан объект потока, если вы не вызываете moveToThread().

Ответ 3

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

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

В этой статье я полагаю, что задержка связана с прямым вызовом, то есть вовсе не обработанным в фоновом режиме (но я только просматривал текст).

Ответ 4

#include <QtGui/QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QString>
#include "mythread.h"
//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    QHBoxLayout * pH = new QHBoxLayout(&w);
    QPushButton * pushButton = new QPushButton("asdad");
    QLineEdit * lineEdit = new QLineEdit("AAA");
    pH->addWidget(pushButton);
    pH->addWidget(lineEdit);
    w.setLayout(pH);
    w.show();
    MyThread thread;
    thread.moveToThread(&thread);
    thread.start();
    qDebug("Thread id %d",(int)QThread::currentThreadId());
    QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(callRun()),Qt::QueuedConnection) ;
    QObject::connect(&thread,SIGNAL(signalGUI(QString)),lineEdit,SLOT(setText(QString)),Qt::DirectConnection);
    return a.exec();
}

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QMutex>

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread();
public slots:
    void callRun();
    void run();
 signals:
    void signalGUI(QString);
private:
    QMutex mutex;

};

#endif // MYTHREAD_H
#include "mythread.h"
#include <QDebug>
#include <QString>
#include <QMutexLocker>

MyThread::MyThread()
{
}
 void MyThread::callRun()
 {
     QMutexLocker fn_scope(&mutex);
     static int a = 0;
    ++a;
     qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
     this->sleep(3);
     static QString number;
     QString temp;
     number += temp.setNum(a);
     emit signalGUI(number);

 }
 void MyThread::run()
 {
    exec();
 }

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

Ответ 5

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