Object thread and function execution thread in Qt multithreaded programming

    Recently, I used Qt to write a multi-threaded TcpSocket communication program. I was confused by several warnings reported in Qt. It said "Cannot create children for a parent that is in a different thread" for a while, and sometimes "QSocketNotifier: socket notifiers cannot be enabled from another thread", and often Assert failure: Cannot send events to objects owned by a different thread, causing the program to crash.

    In order to thoroughly understand the reason and solve the problem, after consulting a large number of materials and Qt documents, I clarified the mechanism, and also have a clearer understanding of QObject object creation and connect execution in multi-threaded programming:

    1. The thread of an object is the thread when the object was created, regardless of which thread the object's definition is stored in;

    2. QObject's connect function has several connection methods,

      a) DirectConnection, the slot function is executed immediately after the signal is sent, and is executed by the thread where the sender is located;

      b) QueuedConnection, returns after the signal is sent, and the related slot function is executed by the thread where the receiver is located after returning to the event loop;

      c) Qt::AutoConnection is used by default. When the sender and receiver are in the same thread, the DirectConnection method is used. When the sender and receiver are in different threads, the QueuedConnection method is used.

    In order to understand these issues more clearly, a small example is specially compiled here. First define a class SomeObject inherited from QObject, including a signal someSignal and a member function callEmitSignal, this function is used to send the previous someSignal signal. Defined as follows:

// define Object class
class SomeObject : public QObject
{
    Q_OBJECT
public:
    SomeObject(QObject* parent=0) : QObject(parent) {}
    void callEmitSignal() // function for sending signal
    {
        emit someSignal ();
    }
signals:
    void someSignal();
};

Then define a thread class SubThread inherited from QThread, which contains an object pointer obj of SomeObject, and a slot function someSolt, which is defined as follows:

class SubThread : public QThread
{
    Q_OBJECT
public:
    SubThread(QObject* parent=0) : QThread(parent){}
    virtual ~SubThread()
    {
        if (obj!=NULL) delete obj;
    }
public slots:
    // slot function connected to obj's someSignal
    void someSlot();
public:
    SomeObject * obj;
};
// slot function connected to obj's someSignal
void SubThread::someSlot()
{
    QString msg;
    msg.append(this->metaObject()->className());
    msg.append("::obj's thread is ");
    if (obj->thread() == qApp->thread())
    {
        msg.append("MAIN thread;");
    }
    else if (obj->thread() == this)
    {
        msg.append("SUB thread;");
    }
    else
    {
        msg.append("OTHER thread;");
    }
    msg.append(" someSlot executed in ");
    if (QThread::currentThread() == qApp->thread())
    {
        msg.append("MAIN thread;");
    }
    else if (QThread::currentThread() == this)
    {
        msg.append("SUB thread;");
    }
    else
    {
        msg.append("OTHER thread;");
    }
    qDebug() << msg;
    quit();
}

Here the someSlot function mainly outputs the thread where obj is located and the execution thread of the slot function.

    Then three thread classes are inherited from SubThread, namely SubThread1, SubThread2, SubThread3. The run function of the thread is implemented respectively. Defined as follows:

// define sub thread class 1
class SubThread1 : public SubThread
{
    Q_OBJECT
public:
    SubThread1(QObject* parent=0);
    // reimplement run
    void run();
};
class SubThread2 : public SubThread
{
    Q_OBJECT
public:
    SubThread2(QObject* parent=0);
    // reimplement run
    void run();
};
class SubThread3 : public SubThread
{
    Q_OBJECT
public:
    SubThread3(QObject* parent=0);
    // reimplement run
    void run();
};

    Create 3 different threads in the main program and run them, and view the running results.

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    SubThread1* t1 = new SubThread1(&a); //由主线程创建
    t1->start();
    SubThread2* t2 = new SubThread2(&a); //由主线程创建
    t2->start();
    SubThread3* t3 = new SubThread3(&a); //由主线程创建
    t3->start();
    return a.exec();
}

    下面我们来分析不同写法的程序,其obj对象所在的线程空间和someSlot函数执行的线程空间分别是怎样的。

    首先看SubThread1的实现:

////////////////////////////////////////////////////////
// class SubThread1
////////////////////////////////////////////////////////
SubThread1::SubThread1(QObject* parent)
    : SubThread(parent)
{
    obj = new SomeObject();//由主线程创建
    connect(obj, SIGNAL(someSignal()), this, SLOT(someSlot()));
}
// reimplement run
void SubThread1::run()
{
    obj->callEmitSignal();
    exec();
}

可以看到,obj是在构造函数中被创建的,那么创建obj对象的线程也就是创建SubThread1的线程,一般是主线程,而不是SubThread1所代表的线程。同时由于obj和this(即t1)都位于主线程,所以someSlot函数也是由主线程来执行的。

    而在线程SubThread2中,我们把obj对象的创建放到子线程的run函数中,那么obj对象的线程就应该SubThread2代表的线程,即t2,就不再是主线程了。
////////////////////////////////////////////////////////
// class SubThread2
////////////////////////////////////////////////////////
SubThread2::SubThread2(QObject* parent)
    : SubThread(parent)
{
    obj=0;
}
// reimplement run
void SubThread2::run()
{
    obj = new SomeObject(); //由当前子线程创建
    connect(obj, SIGNAL(someSignal()), this, SLOT(someSlot()));
    obj->callEmitSignal();
    exec();
}

同时,在connect函数中由于obj和this(这里是t2)不是在同一个线程中,因此会采用QueuedConnection的方式,其slot函数由this对象所在的线程即主线程来执行。这里有一个特别容易误解的地方,就是这个slot函数虽然是子线程SubThread2的一个成员函数,connect操作也是在子线程内完成的,但是该函数的执行却不在子线程内,而是在主线程内。

    那么如果想让相应的slot函数在子线程内执行,该如何做呢?在子线程的run函数中创建obj对象的同时,在执行connect时指定连接方式为DirectConnection,这样就可以使slot函数在子线程中运行,因为DirectConnection的方式始终由sender对象的线程执行。如
////////////////////////////////////////////////////////
// class SubThread3
////////////////////////////////////////////////////////
SubThread3::SubThread3(QObject* parent)
    : SubThread(parent)
{
    obj=0;
}
// reimplement run
void SubThread3::run()
{
    obj = new SomeObject();
    connect(obj, SIGNAL(someSignal()), this, SLOT(someSlot()),
            Qt::DirectConnection);
    obj->callEmitSignal();
    exec();
}

    最后,该程序的运行结果应该是:

"SubThread1::obj's thread is MAIN thread; someSlot executed in MAIN thread;" 
"SubThread2::obj's thread is SUB thread; someSlot executed in MAIN thread;" 
"SubThread3::obj's thread is SUB thread; someSlot executed in SUB thread;" 








Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325763824&siteId=291194637