Qthread relationship with Qobject (Qobject can be used for multi-threading, can send signals to call slot functions that exist on other threads, but GUI classes are not reentrant)

Source: Internet
Author: User

Qthread inherit Qobject. It can send started and finished signals, and also provides some slot functions.

Qobject. Can be used for multiple threads, you can send a signal to call a slot function that exists in another thread, or you can postevent to an object in another thread. This is possible because each thread has its own event loop.

Before doing this, it is important to understand that the thread on which the Qthread object is located, and the thread created by Qthread, which is the thread that the run () function executes, is not the same thread. The thread on which the Qthread object resides is the thread that created the object. Let's make it clearer by an example:

Mythread::mythread (qobject *parent/* = NULL */): Qthread (parent)

{

Qdebug () << "Mythreadobject currentthreadid:" <<qthread::currentthreadid ();

}

void Mythread::run ()

{

Qdebug () << "Run () Currentthreadid:" <<qthread::currentthreadid ();

}

int main (int argc, char *argv[])

{

Qapplication A (argc, argv);

MyThread thread;

Qdebug () << "Mainthread:" <<qthread::currentthreadid ();

Thread.Start ();

Returna.exec ();

}

Output: The thread where the Mythread is located is the primary thread, and the run () function is the newly opened one.

Qobject reentrancy

Qobject. is reentrant, and most of its non-GUI subclasses, such as Qtimer, Qtcpsocket, Qudpsocket and qprocess, are reentrant, making these classes available for multithreading at the same time. It is important to note that these classes are designed to be created and used from a single thread, creating objects in one thread, and calling objects from another thread is not guaranteed to work. There are three limitations to be aware of:

1. The child object of the Qobject must be created in the thread that created its parent. This means that you cannot pass the Qthread object as a parent to the object that was created in the thread, because the Qthread object itself is created in a different one.

2. Event-driven objects can only be used for single threads. Especially in the timer mechanism and the network module. For example, you cannot start a timer or link a secket on a thread that is not where the object is located. Simply put, you can't create a timer on thread A, and then thread B starts the timer from the start.

We can verify that:

Class Mythread:publicqthread

{

Q_object

Public

MyThread (Qobject *parent = NULL);

~mythread ();

Public Slots:

Voidtimeoutslot ();

Protected

Voidrun ();

Qtimer *m_ptimer;

};

Mythread::mythread (qobject*parent/* = NULL */): Qthread (parent)

{

M_ptimer = Newqtimer (this);

Qdebug () << "Mythreadobject currentthreadid:" <<qthread::currentthreadid ();

Connect (m_ptimer,signal ()-Timeout ()), This,slot (Timeoutslot ()));

}

void Mythread::timeoutslot ()

{

Qdebug () << "Timer timeout";

}

Mythread::~mythread ()

{

}

void Mythread::run ()

{

M_ptimer->start (500);

Qdebug () << "Run () Currentthreadid:" <<qthread::currentthreadid ();

Qdebug ("finish!");

}

Intmain (int argc, char*argv[])

{

Qapplication A (argc, argv);

MyThread thread;

Qdebug () << "Mainthread:" <<qthread::currentthreadid ();

Thread.Start ();

Returna.exec ();

}

The timeout function is not called. We also found a line of output: Qobject::starttimer:timers cannot be startedfrom another thread

Tracking Timer's start source code, we found:

void Qeventdispatcherwin32::registertimer (int timerid, intinterval, Qobject *object)

{

if (timerid< 1 | | Interval < 0 | |!object) {

Qwarning ("qeventdispatcherwin32::registertimer:invalid arguments");

Return

}

else if (object->thread ()! = Thread () | | thread ()! = Qthread::currentthread ())

{

The thread of the object, which is the thread of the object, does not equal the current threads to return

Qwarning ("Qobject::starttimer:timers cannot is started from another thread");

Return

}

。。。。。

}

3. You must ensure that the objects created in the thread are destroyed before the thread destroys the delete. This is easy to do, as long as the object created in the run () function stack is on the line.

Although Qobject is reentrant, GUI classes, especially Qwidget and its subclasses, are non-reentrant. They can only be used in the main thread. As mentioned earlier, qcoreapplication::exec () must be called from the main thread.

Per-thread Event Loop

Each thread has its own event loop. The starting thread turns on the event loop with Qcoreapplication::exec (). Other threads start the event loop with Qthread::exec (). Like Qcoreapplication, Qthread also provides the exit (int) function and the Quit () slot function.

The event loop of the line thread makes it possible to use non-GUI classes that require event loops in the thread, such as (Qtimer, Qtcpsocket, and qprocess). You can also connect the signals of any thread to the slots of a particular thread.

The Qobject instance exists in the thread that created the instance, and the event that is sent to the instance is also implemented by the event loop of the wired thread. You can use Qobject::thread (). Gets the thread on which the object survives.

Mythread::mythread (qobject*parent/* = NULL */): Qthread (parent)

{

M_ptimer = Newqtimer (this);

Qdebug () << "Mythreadobject currentthreadid:" <<qthread::currentthread ();

Qobject obj1;

Obj1.thread ();

Qdebug () << "obj1live in the Thread:" <<obj1.thread ();

Connect (m_ptimer,signal ()-Timeout ()), This,slot (Timeoutslot ()));

Qthread::start ();

}

void Mythread::run ()

{

Qobject Obj2;

Obj2.thread ();

Qdebug () << "button2live in the Thread:" <<obj2.thread ();

M_ptimer->start (500);

Qdebug () << "Run () Currentthreadid:" <<qthread::currentthread ();

Qdebug ("finish!");

}

This once again illustrates that the thread on which the object is located is the thread that created it.

Note: For those objects created before qapplication, Qobject::thread () returns 0. This means that the main thread handles only the events that are sent to those objects, and those objects that do not have thread do not do any event processing. Use the Qobject::movetothread () function to change the thread affinity of an object and its sub-objects, which is simply to move the object from the current thread to another thread. But if an object already has the parent, it can't move.

It is not safe to call Delete to delete a Qobject object that is in another thread. Unless you can guarantee that the object is not currently in the process of event handling. It should be replaced with Qobject::d Eletelater (), and a Deferreddelete event will be emitted, which will eventually be captured by the time loop of the object's thread.

If there is no time loop, there will be no event passed to the object. For example, if you create a Qtimer object in a thread, but do not call EXEC (), then Qtimer never emits a timeout () signal, and calling Eletelater () does not work.

void Mythread::run ()

{

M_ptimer = Newqtimer ();

M_ptimer->start (500);

Connect (m_ptimer,signal ()-Timeout ()), This,slot (Timeoutslot ()));

Qdebug () << "Run () Currentthreadid:" <<qthread::currentthread ();

This->exec ();

Qdebug ("finish!");

}

void Mythread::timeoutslot ()

{

Qdebug () << "Timer timeout";

M_ptimer->stop ();

}

This is the time to call Timeoutslot ().

void Mythread::run ()

{

M_ptimer = Newqtimer ();

M_ptimer->start (500);

Connect (m_ptimer,signal ()-Timeout ()), This,slot (Timeoutslot ()));

Qdebug () << "Run () Currentthreadid:" <<qthread::currentthread ();

This->exec ();

Qdebug ("finish!");

}

If the comment//this->exec (), Timeoutslot () will not be called.

It is also important to note that the Qtimer object cannot stop on another thread. If you put the M_ptimer->stop () in the Timeoutslot, uncomment it. You see a line of output: Qobject::killtimer:timers cannot be stopped fromanother thread

In the source code:

BOOL Qeventdispatcherwin32::unregistertimer (int timerid)

{

if (timerid< 1) {

Qwarning ("qeventdispatcherwin32::unregistertimer:invalid argument");

return false;

}

Qthread *currentthread = Qthread::currentthread ();

Determines whether the thread in which the timer is located is consistent with the current thread.

if (thread ()! = CurrentThread)

{

Qwarning ("Qobject::killtimer:timers cannot is stopped from another thread");

return false;

}

。。。。

}

You can use Qcoreapplication::p the Ostevent () function to send events to any object in any thread at any time. Events are automatically distributed by the event loop of the thread that created the object. So all threads support event filters, and the only limitation is that the monitoring object must be on the same thread as the monitored object. Similarly, qcoreapplication::sendevent () can only be used to send an event to an object that is on the same thread as the Qcoreapplication::sendevent () function. To put it bluntly, qcoreapplication::sendevent () cannot send an event to an object that is in a different thread.

Accessing qobjectsubclasses from other Threads

Qobject and all of its subclasses are not thread-safe. This includes the entire event delivery system, and it's important to remember that the event loop may be sending an event to an object, and you might be accessing the object from another thread.

If you call a function that is not a subclass object Qobject the current thread, and when the object may receive an event, you must protect the object's intrinsic data with a mutex. Otherwise, it can cause a program to crash or undefined behavior.

Like other objects, the Qthread object survives in the thread that created the object, not the Qthread::run () thread. This point is mentioned earlier. It is not safe to provide a slot function in a custom qthread subclass unless you protect the member variable with a mutex. However, you can signal in the implementation of the Qthread::run () because the signal transmission is thread-safe.

Signals and Slots Acrossthreads

QT supports several signal-slot connection methods:

1. Auto Connection (default): If the sender of the signal is on the same thread as the receiver, the connection is Direct Connection, otherwise it is the same as Queued Connection.

2. Direct Connection: When the signal is sent, the slot is immediately called. The slot function is run in the thread of the sender of the signal and does not require a thread from the receiving party.

3. Queued Connection: The slot function is called when control returns to the receiver thread. The slot function is run in the thread of the receiving party.

4. Blocking Queued Connection: The call mode is the same as Queued Connection, except that the current thread is blocked until the slot function returns.

5. Unique Connection: This is the same way as Auto Connection, but a connection is created only if there is no same connection. If the same connection already exists, the connection is not created, and connect () returns false.

You can specify the connection type in the Connect () add parameter. One thing to note is that if the signal sender and receiver are in different threads, and the receiver thread is running an event loop, it is unsafe to use direct connection because it is not safe to call an object's function, and the object is in another thread, and that call is unsafe.

The Qobject::connect () itself is thread-safe.

Here's a sample of the results to verify:

Class Receiver:publicqobject

{

Q_object

Public

Voidsendmes ()

{

Emitemitsignal ("emit message from A to B");

}

Receiver ()

{

}

Protected Slots:

Voidmessageslot (QString mes)

{

Qdebug () <<mes;

}

Signals:

Voidemitsignal (QString mes);

Private

};

int main (int argc, char *argv[])

{

Qapplication A (argc, argv);

Receiver OBJA,OBJB;

Qobject::connect (&obja,signal (emitsignal (QString)), &objb,slot (Messageslot (QString)));

Qdebug () << "beforeemitsignal";

Obja.sendmes ();

Qdebug () << "afteremitsignal";

Returna.exec ();

}

OBJA,OBJB; for the same thread, the connection type of connect is direct Connection

From the output we can see the order of execution,

If we write two lines of connection:

Qobject::connect (&obja,signal (emitsignal (QString)), &objb,slot (Messageslot (QString)));

Qobject::connect (&obja,signal (emitsignal (QString)), &objb,slot (Messageslot (QString)));

There will be two messages output:

If you specify a connection type of qt::uniqueconnection, only one message is output.

Qobject::connect (&obja,signal (emitsignal (QString)), &objb,slot (Messageslot (QString)), Qt:: Uniqueconnection);

Qobject::connect (&obja,signal (emitsignal (QString)), &objb,slot (Messageslot (QString)), Qt:: Uniqueconnection);

int main (int argc, char *argv[])

{

Qapplication A (argc, argv);

Qthread *thread = new Qthread;

Thread->start ();

Receiver OBJA,OBJB;

Objb.movetothread (thread);

Qobject::connect (&obja,signal (emitsignal (QString)), &objb,slot (Messageslot (QString)));

Qdebug () << "beforeemitsignal";

Obja.sendmes ();

Qdebug () << "afteremitsignal";

Returna.exec ();

}

If we put OBJB on another thread, the connection type of connect should be queued Connection.

int main (int argc, char *argv[])

{

Qapplication A (argc, argv);

Qthread *thread = new Qthread;

Thread->start ();

Receiver OBJA,OBJB;

Objb.movetothread (thread);

Qobject::connect (&obja,signal (emitsignal (QString)), &objb,slot (Messageslot (QString)), Qt:: Blockingqueuedconnection);

Qdebug () << "beforeemitsignal";

Obja.sendmes ();

Qdebug () << "afteremitsignal";

Returna.exec ();

}

The specified connection type shown is Qt::blockingqueuedconnection, and the output is:

http://blog.csdn.net/hai200501019/article/details/9748173

Qthread relationship with Qobject (Qobject can be used for multi-threading, can send signals to call slot functions that exist on other threads, but GUI classes are not reentrant)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.