QT, сигнализирующий через потоки, каждый - поток GUI?

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

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

Править: Я сделал небольшую программу, но я не понимаю, как QThread работает наряду с Сигналом и функцией слота, я ценил бы, если Вы могли бы объяснить, что является использованием 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);
 }
28
задан yesraaj 24 January 2010 в 07:03
поделиться

5 ответов

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

struct IFoo 
{
    virtual void foo() = 0;
};

struct IBar 
{
    virtual void bar() = 0;
};

struct IFooAndBar : public IFoo, public IBar {};

class FooAndBarCompositor : public IFooAndBar
{
public:
    template <class T>
    FooAndBarCompositor(T* pImpl) : m_pFoo(pImpl), m_pBar(pImpl) {}

    void foo() {m_pFoo->foo();}
    void bar() {m_pBar->bar();}

private:
    IFoo* m_pFoo;
    IBar* m_pBar;
};

Затем вы пишете функцию, которая принимает IFoaAndBar *, если требуются оба интерфейса, и вызывающий может создать FooAndBarCompositor на стеке, который отправляется на выбранный объект. Выглядит так:

void testFooAndBar(IFooAndBar* pI) {}

void baz(Fred* pFred)
{
    FooAndBarCompositor fb(pFred);
    testFooAndBar(&fb);
}

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

template <class IA, class IB>
class InterfaceCompositor
{
public:
    template <class T>
    InterfaceCompositor(T* pObj) : m_pIA(pObj), m_pIB(pObj) {}

    IA* AsA() const {return m_pIA;}
    operator IA* () const {return AsA();}
    IB* AsB() cosnt {return m_pIB;}
    operator IB* () const {return AsB();}

private:
    IA* m_pIA;
    IB* m_pIB;
};

Тогда функция выглядит следующим образом:

void testFooAndBar(InterfaceCompositor<IFoo, IBar> pI)
{
    IFoo* pFoo = pI; // Or pI.AsA();
    IBar* pBar = pI; // Of pI.AsB();
}

Для этого требуется, чтобы функция, которая хочет принудительно использовать несколько интерфейсов, либо использовала составитель, где ожидается A * или B * (например, назначение или параметр функции), либо явно вызывала соответствующий метод AsX (). В частности, используемый интерфейс не может быть выведен из использования оператора - >, и оператор * не имеет значения для составного элемента.

При использовании общего кода можно использовать тот же шаблон для обеспечения поддержки объектом как IBar, так и IBaz.

C++ 0x введет вариадные шаблоны, которые позволят расширить эту концепцию на произвольные числа классов интерфейсов.

-121--4716590-

Используется ли метод JTextArea append (Последовательность) для добавления дополнительного текста?

JTextArea txtArea = new JTextArea("Hello, World\n", 20, 20);
txtArea.append("Goodbye Cruel World\n");
-121--1555920-

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

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

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

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

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

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

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

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

37
ответ дан 28 November 2019 в 03:13
поделиться

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

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

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

3
ответ дан 28 November 2019 в 03:13
поделиться
#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();
 }

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

3
ответ дан 28 November 2019 в 03:13
поделиться

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

0
ответ дан 28 November 2019 в 03:13
поделиться
  1. Метод QTREAD :: Mether () Создает нить и вызывает вашу работу RUN () . Если вы хотите обрабатывать события или получать сигналы в потоке, вы должны вызвать QTHTREAD :: EXEC () внутри Ваш RUN () . Вы никогда не должны звонить Run () явно, и вы никогда не должны вызывать Exec () за пределами RUN () .

  2. Поток владельца имеет значение только тогда, когда слот подключен к сигналу с типом соединения, отличным от Qt :: DirectConnection . Тогда Qt будет гарантировать, что слот проходит в потоке владельца, но для того, чтобы поток владельца должен выполнять петлю событий с QTREAD :: EXEC () . В этом случае призвание myobj.movetoThread (mythread) будет обеспечивать, чтобы пробег Myobj пробежал на потоке Mythread .

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

8
ответ дан 28 November 2019 в 03:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: