Multi-thread programming of QT Learning

Source: Internet
Author: User
Tags emit mutex shallow copy thread class

QT provides support for threading in three different forms. Each of them is,

One, platform-independent threading class
Second, thread-safe event delivery
Three, a signal-slot connection across threads.

This makes it easier to develop lightweight, multithreaded QT programs, and maximizes the benefits of multi-processor machines.

Multithreaded programming is also a practical mode. It is used to resolve operations that run for a long time without the user interface losing response.

In the previous version number of QT. There is no option to select thread support when building the library, and the thread is always valid from 4.0.

Thread class

Qt includes some of the following thread-related classes:

    • Qthread provides a way to start a new thread
    • Qthreadstorage provides per-thread data storage
    • Qmutex offers mutually exclusive locks, or mutually exclusive amounts
    • Qmutexlocker is a convenient class that can take its own initiative to Qmutex locking and unlock
    • Qreadwriterlock provides a lock that can read operations at the same time
    • Qreadlocker and Qwritelocker are convenience classes. Its own initiative to qreadwritelock locking and unlock
    • Qsemaphore provides an integral type of semaphore, which is a generalization of mutually exclusive quantities
    • Qwaitcondition provides a way to do this. Allows a thread to hibernate until it is awakened by another thread.
Create a thread

This article has a more humane display on this issue.

To create a thread. Subclass the Qthread and rewrite its run () function, for example:

class MyThread : public QThread{     Q_OBJECTprotected:     void run();};void MyThread::run(){     ...}

After that, the instance of this thread object is created. Call Qthread::start (). So. The code that appears in run () will be run in another thread.

Note: qcoreapplication::exec () must always be called in the main thread (the one that runs main ()) and cannot be called from a qthread. In the GUI program. The main thread is also known as a GUI thread, because it is the only thread that agrees to run GUI-related operations.

In addition, you must create a qapplication (or qcoreapplication) object before creating a qthread.

Thread synchronization

The sync mode here is to look at these two articles, which provide a link to the next article.

Qmutex, Qreadwritelock, Qsemaphore, qwaitcondition provide a means of thread synchronization. The main idea of using threads is to expect them to run as concurrently as possible, while some key points need to be stopped or waiting between them.

For example, suppose two threads attempt to access the same global variable at the same time. The results may not be as good as you wish.

Qmutex provide mutually exclusive locks, or mutually exclusive amounts. At most one thread has a mutex at a time, and if a thread attempts to access a mutex that is already locked, it will hibernate until the thread that owns the mutex unlocks the mutex.

Mutexes is often used to protect shared data access.
Qreadwriterlock is similar to Qmutex, except that it treats "read" and "write" interviews differently. It enables multiple readers to access data in a shared time. Use Qreadwritelock instead of Qmutex. Enables multi-threaded routines to be more concurrent.

QReadWriteLock lock;void ReaderThread::run(){    ...     lock.lockForRead();     read_file();     lock.unlock();     //...}void WriterThread::run(){   ...     lock.lockForWrite();     write_file();     lock.unlock();    ...}

Qsemaphore is the generalization of Qmutex, which can protect a certain number of identical resources. Relative to this. A mutex protects only one resource.

In the following example, Qsemaphore is used to control access to the ring buffer, which is shared by the producer thread and the consumer thread. Producers continue to write data to the buffer until the end of the buffer, and then start from the beginning. Consumers constantly read data from buffers. The signal volume is better than the mutual rejection of the concurrency, if we use mutual exclusion amount to control the access to the buffer, then producers, consumers can not visit the buffer at the same time.

However. We know at the same moment. Different threads have no harm in visiting different parts of the buffer.

Const intDataSize =100000;Const intBufferSize =8192;CharBuffer[buffersize]; Qsemaphore freebytes (buffersize); Qsemaphore Usedbytes;class Producer: Publicqthread{ Public:void Run();};voidProducer::run () {Qsrand (Qtime (0,0,0). Secsto (Qtime::currenttime ())); for(inti =0; i < datasize;         ++i) {freebytes.acquire (); Buffer[i% buffersize] ="ACGT"[(int) Qrand ()%4];     Usedbytes.release (); }}class Consumer: Publicqthread{ Public:void Run();};voidConsumer::run () { for(inti =0; i < datasize;         ++i) {usedbytes.acquire (); fprintf (stderr,"%c", buffer[i% buffersize]);     Freebytes.release (); } fprintf (stderr,"\ n");}intMainintargcChar*argv[]) {qcoreapplication app (argc, argv);     Producer Producer;     Consumer Consumer;     Producer.start ();     Consumer.start ();     Producer.wait (); Consumer.wait ();return 0;}

Qwaitcondition agrees that the thread wakes up another thread when certain conditions occur. One or more threads can block waiting for a qwaitcondition, setting a condition with wakeone () or Wakeall ().

Wakeone () randomly wakes one, wakeall () wakes all.

In the following example. The producer must first check whether the buffer is full (numusedbytes==buffersize), assuming that the thread stops to wait for the buffernotfull condition.

If not, produce the data in the buffer. Add numusedbytes to activate the condition buffernotempty. Use mutexes to protect access to numusedbytes.

In addition, qwaitcondition::wait () receives a mutex as a parameter, and the mutex should be initialized to a locked state by the calling thread. The mutex is unlocked before the thread enters hibernation.

And when the thread is awakened. The mutex is locked, and the transition from the lock state to the wait state is atomic, which prevents the race condition from being generated. When a program starts running, only producers can work. Consumers are blocked waiting for buffernotempty conditions once the producer puts a byte in the buffer. Buffernotempty conditions are fired. The consumer thread is then awakened.

Const intDataSize =100000;Const intBufferSize =8192;CharBuffer[buffersize]; Qwaitcondition Buffernotempty; Qwaitcondition Buffernotfull; Qmutex Mutex;intNumusedbytes =0; Class Producer: Publicqthread{ Public:void Run();};voidProducer::run () {Qsrand (Qtime (0,0,0). Secsto (Qtime::currenttime ())); for(inti =0; i < datasize; ++i) {Mutex.Lock();if(numusedbytes = = buffersize) buffernotfull.wait (&mutex);         Mutex.unlock (); Buffer[i% buffersize] ="ACGT"[(int) Qrand ()%4]; Mutex.Lock();         ++numusedbytes;         Buffernotempty.wakeall ();     Mutex.unlock (); }}class Consumer: Publicqthread{ Public:void Run();};voidConsumer::run () { for(inti =0; i < datasize; ++i) {Mutex.Lock();if(Numusedbytes = =0) buffernotempty.wait (&mutex);         Mutex.unlock (); fprintf (stderr,"%c", buffer[i% buffersize]); Mutex.Lock();         --numusedbytes;         Buffernotfull.wakeall ();     Mutex.unlock (); } fprintf (stderr,"\ n");}intMainintargcChar*argv[]) {qcoreapplication app (argc, argv);     Producer Producer;     Consumer Consumer;     Producer.start ();     Consumer.start ();     Producer.wait (); Consumer.wait ();return 0;}
Reentrant and thread safe

In the QT documentation. The term "reentrant" and "thread safe" are used to describe how a function can be used in multithreaded programs. A class can be called by multiple threads at the same time, assuming that no matter what function is on many different instances of this class. Then this class is referred to as "reentrant". If the different threads function on the same instance, they will still work correctly. So called "thread-safe".
Most C + + classes are inherently reentrant. Because they typically only refer to member data. No matter what thread is able to invoke such a member function on an instance of the class, no other thread calls this member function on the same instance. For example, the following counter classes are reentrant:

class Counter{public:      Counter() {n=0;}      void increment() {++n;}      void decrement() {--n;}      intvalueconst {return n;}private:      int n;};

This class is not thread-safe, because if multiple threads are trying to change the data member N, the result is undefined. This is because the + + and – operators in C + + are not atomic operations.

In fact, they will be expanded to three machine instructions:

1. Load the variable value into the Register
2. Add or decrease a value in a register
3. Writes the value in the register back to memory

If thread A and B load the old value of the variable at the same time, add value in the register and write back. Their writing operations overlap. Causes the variable value to be added only once.

Very obvious. The interview should be serialized: A should not be interrupted when running 123 steps. The simplest way to make this class A thread safe is to use Qmutex to protect data members:

class Counter{public:     Counter0; }     void increment() { QMutexLocker locker(&mutex); ++n; }     void decrement() { QMutexLocker locker(&mutex); --n; }     intvalueconstreturn n; }private:     mutable QMutex mutex;     int n;};

The Qmutexlocker class actively locks the mutex itself in the constructor, and unlocks it in the destructor.

It's just a matter of mentioning. The mutex is decorated with the mutable keyword, since we lock and unlock the mutex in the value () function, and value () is a const function.
Most QT classes are reentrant. Non-thread-safe. There are classes and functions that are thread-safe, primarily thread-related classes, such as qmutex,qcoreapplication::p ostevent ().

Threading and Qobjects

Qthread inherits from Qobject, which emits signals to instruct the thread to start and end, and also provides a lot of slots. More interestingly, qobjects can be used for multi-threading, because each thread is agreed to have its own event loop.
Qobject Re-entry
Qobject can be re-entered.

Most of its non-GUI subclasses, such as Qtimer,qtcpsocket,qudpsocket,qhttp,qftp,qprocess, are also reentrant, and it is possible to use these classes at the same time in multiple threads. It is important to note that. These classes are designed to be created and used in a single thread, so. A function that creates an object in one thread and invokes it in another thread does not guarantee good work.

There are three types of constraints that need to be noted:
1. Qobject's child should always be created in the thread that his father was created in.

This means that you should never pass the Qthread object as the father of an object (since the Qthread object itself is created in a thread)
2, event-driven objects are only used in a single thread. Clearly, this rule applies to "timer mechanism" and "grid module", for example, you should not start a timer or connect a socket in a thread. When this thread is not the thread on which these objects reside.
3, you must ensure that all objects created in the thread are deleted before you delete Qthread.

This is very easy to do: You can create an object on the stack running the run () function.

Although Qobject is reentrant, the GUI class, especially Qwidget and all its subclasses, are non-reentrant. They are used only for the main thread.

As mentioned earlier, Qcoreapplication::exec () must also be called from that thread.

In practice, GUI classes are not used in other threads, they work on the main thread, and some time-consuming operations are put into separate worker threads. When the worker thread finishes running, the results are displayed on the screen owned by the main thread.

Per-thread event loop

Each thread can have its own event loop, and the initial thread starts its event loop using Qcoreapplication::exec (), and the other thread starts its event loop with Qthread::exec (). Like Qcoreapplication, QTHREADR provides the exit (int) function. A quit () slot.

Event loops in threads that enable threads to use non-GUI classes that require event loops, such as. qtimer,qtcpsocket,qprocess).

It is also possible to connect the signals of whatever thread to the slots of a particular thread, which means that the signal-slot mechanism can be used across threads. For objects that were created before qapplication. Qobject::thread () returns 0, which means that the main thread handles only the post events for these objects. No additional events are handled for objects that do not have a owning thread.

The ability to use Qobject::movetothread () to change the thread affinity between it and its children. If the object has a father. It cannot move such a relationship. It is not safe to delete a Qobject object in a thread other than the one that created it. Unless you can guarantee that the object is not handling events at the same time.

Able to use Qobject::d eletelater (), it will post a Deferreddelete event, which is finally picked up by the event loop of the object thread.

If there is no event loop running, the event is not distributed to the object. For example, if you create a Qtimer object in a thread, but never call exec (), then Qtimer will not emit its timeout () signal. Nor does it work for Deletelater ().

(This same applies to the main thread). You can manually use the thread-safe function qcoreapplication::p ostevent (). At any given time, an event is dispatched to the thread that created the object, regardless of what thread the object is posted in.

Event filters are also supported on all threads. It only restricts the monitored object to live in the same thread as the monitored object. Similarly, qcoreapplication::sendevent (not postevent ()) is used only to post events to the target object in the thread that called the function.

Access the Qobject subclass from another thread

Qobject and all its subclasses are non-thread-safe. This includes the entire event delivery system. It is worth remembering that the event loop is able to deliver events to your Qobject subclass when you are interviewing objects from other threads. If you call a function that does not live in the Qobject subclass of the current thread, you must protect the internal data of the Qobject subclass with a mutex, or you will encounter a disaster or unintended result.

Like other objects, the Qthread object lives in the thread that created it-not the one that was created when Qthread::run () was called.

In general, it is not safe to provide slots in your Qthread subclass unless you protect your member variables with a mutex.

On the other hand, you can safely emit signals from the implementation of the Qthread::run (), because the signal is thread-safe to emit.

Signals across threads-slots

QT supports three types of signal-slot connections:

1. Direct connection, when the signal is fired, the slot is called immediately.

This slot is run in the same thread that launched the signal (not necessarily the one that receives the object's survival)
2, queue connection. When control returns to the event loop of the thread to which the object belongs. Slot is called. This slot is run in the same thread that receives the object's survival
3, self-active connection (default). If a signal is emitted with the receiver in the same thread, its behavior is as direct as the connection. Otherwise, its behavior is like a queue connection.

The connection type may be specified by passing a number of references to connect (). Note that when the sender and receiver live in different threads, and the event loop is running on the recipient's thread, it is unsafe to use a direct connection. Same truth. It is also not safe to call a function that lives on an object in a different thread. The Qobject::connect () itself is thread-safe.

Multi-threading and implicit sharing

QT uses so-called implicit sharing (implicit sharing) for many of its value types to optimize performance. The principle is simpler, and the shared class includes a pointer to the shared data block, which includes the actual original data and a reference count. Translates a deep copy into a shallow copy, which improves performance. Such a mechanism works behind the scenes, and procedural apes do not need to care about it. Suppose the object needs to make changes to the data, and the reference count is greater than 1. Then it should first detach (). So that it doesn't have an impact on other people who share it. Since the changed data and the original data is different, so it is impossible to share, so it first run a deep copy, take the data back. And then make the changes on this piece of data.

Like what:

void QPen::setStyle(Qt::PenStyle style){     detach();           detach from common data     d->style = style;   // set the style member}void QPen::detach(){     if1) {         ...             // perform a deep copy     }}

It is generally felt that implicit sharing and multithreading are not very harmonious due to the existence of a reference count. One of the ways to protect a reference count is to use a mutex, but it is very slow. The early version of Qt did not provide a comfortable solution. Start from 4.0. Implicit shared classes are able to safely copy across threads. Same as other value types.

They are fully reentrant.

Implicit sharing is really "implicit".

It uses assembly language to implement atomic reference counting operations, which is much faster than using mutexes.
If you are interviewing the same object in multiple threads, you also need to serialize the order of the access using a mutex, just like any other reentrant object. In general, implicit sharing really gives "implied" off, in multithreaded programs. You can think of them as generic, non-shared, reentrant types that are safe.

Multi-thread programming of QT Learning

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: 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.