Как излучать сигнал перекрестной нити в Qt?


Документация Qt утверждает, что сигналы и слоты Могут быть direct, queued и auto.

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

К сожалению, в документации не указано, что означает "жизнь", и нет примеров. Я попробовал следующее код:

main.h:

class CThread1 : public QThread
{
Q_OBJECT
public:
    void run( void )
    {
        msleep( 200 );
        std::cout << "thread 1 started" << std::endl;
        MySignal();
        exec();
    }
signals:
    void MySignal( void );
};

class CThread2 : public QThread
{
Q_OBJECT
public:
    void run( void )
    {
        std::cout << "thread 2 started" << std::endl;
        exec();
    }
public slots:
    void MySlot( void )
    {
        std::cout << "slot called" << std::endl;
    }
};

main.cpp:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    CThread1 oThread1;
    CThread2 oThread2;
    QObject::connect( & oThread1, SIGNAL( MySignal() ),
        & oThread2, SLOT( MySlot() ) );
    oThread1.start();
    oThread2.start();
    oThread1.wait();
    oThread2.wait();
    return a.exec();
}

Вывод:

thread 2 started
thread 1 started

MySlot() никогда не называется :(. Что я делаю не так?

2 57

2 ответа:

Существует довольно много проблем с вашим кодом:

  • Как сказал Эван ключевое слово emit отсутствует
  • все ваши объекты живут в основном потоке, только код в методах run живет в других потоках, что означает, что слот MySlot будет вызван в основном потоке, и я не уверен, что это то, что вы хотите
  • Ваш слот никогда не будет вызван, так как основной цикл событий никогда не будет запущен : ваши два вызова wait () будут только таймаут после очень долгого времени (и вы, вероятно, убьете свое приложение, прежде чем это произойдет), и я не думаю, что это то, что вы хотите, в любом случае, они действительно не имеют смысла в вашем коде.

Этот код, скорее всего, будет работать (хотя я его не тестировал) , и я думаю, что он делает то, что вы хотите, чтобы он сделал :

class MyObject : public QObject
{
    Q_OBJECT
public slots:
    void MySlot( void )
    {
        std::cout << "slot called" << std::endl;
    }
};

class CThread1 : public QThread
{
    Q_OBJECT
public:
    void run( void )
    {
        std::cout << "thread 1 started" << std::endl;
        int i = 0;
        while(1)
        {
           msleep( 200 );
           i++;
           if(i==1000)
              emit MySignal();
        }
    }
signals:
    void MySignal( void );
};

class CThread2 : public QThread
{
    Q_OBJECT
public:
    void run( void )
    {
        std::cout << "thread 2 started" << std::endl;
        exec();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    CThread1 oThread1;
    CThread2 oThread2;
    MyObject myObject;
    QObject::connect( & oThread1, SIGNAL( MySignal() ),
        & myObject, SLOT( MySlot() ) );
    oThread2.start();
    myObject.moveToThread(&oThread2)
    oThread1.start();
    return a.exec();
}

Теперь MyObject будет жить в thread2 (благодаря moveToThread).

MySignal должен быть отправлен из thread1 (подумал, что я не уверен в этом, он может быть отправлен из основного потока, на самом деле это не так вопрос).

В thread1 не требуется цикл событий, так как излучение сигнала не нуждается в цикле событий. Чтобы получить сигнал, в thread2 необходим цикл событий (подключенный exec ()).

MySlot будет называться в thread2.

Не подкласс QThread для Qt 4.4+

Хотя ответ Aiua хорош, я хочу указать на некоторые проблемы с QThread и Qt 4.6 или 4.7.

Эта статья подводит итог: http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/

Отсутствие документации со стороны Qt

К сожалению, проблема связана с отсутствием обновлений документации. До Qt 4.4 QThread не имел реализации run () по умолчанию, что означало, что вы должны были подкласс QThread для того, чтобы использовать его.

Если вы используете Qt 4.6 или 4.7, то вы почти наверняка должны Не подкласс QThread.

Использовать moveToThread

Ключом к получению слотов для выполнения в рабочем потоке является использование метода moveToThread, как указала Aiua.