Qt multithreaded Programming

Source: Internet
Author: User

See a good article, specially reproduced down, very detailed very comprehensive, collection of.

Original address: http://blog.21ic.com/user1/1425/archives/2009/64057.html

QT multithreaded ProgrammingQT provides support for threading in three different forms. They are, one, platform-independent thread class, two, thread-safe event delivery, three, cross-thread signal-slot connection. This makes it easier to develop a lightweight multithreaded QT program and take advantage of multiprocessor machines. Multithreaded programming is also a useful pattern that is used to resolve long-run operations without the user interface losing response. In earlier versions of QT, there was no option to select thread support when building the library, and the thread was always valid starting with 4.0.

Thread Class

Qt contains 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 mutexes
Qmutexlocker is a handy class that can automatically locking and unlock Qmutex
Qreadwriterlock provides a lock that can read operations simultaneously
Qreadlocker and Qwritelocker are convenience classes, which automatically locking and unlocks Qreadwritelock
Qsemaphore provides an integer semaphore, which is a generalization of the mutex
Qwaitcondition provides a way for a thread to hibernate until it is awakened by another thread.

Create a thread

To create a thread, subclass the Qthread and rewrite its run () function, for example:
Class Mythread:public Qthread
{
Q_object

Protected
void run ();
};

void Mythread::run ()
{
...
}
After that, create an instance of this thread object, calling Qthread::start (). The code that appears in run () will then be executed in the other thread.
Note: qcoreapplication::exec () must always be called in the main thread (the one that executes main ()) and cannot be called from a qthread. In a GUI program, the main thread is also called a GUI thread, because it is the only thread that allows GUI-related operations to be performed. In addition, you must create a qapplication (or qcoreapplication) object before creating a qthread.

thread synchronization

Qmutex, qreadwritelock,  QSemaphore, QWaitCondition  provides a means of thread synchronization. The main idea of using threads is to expect them to be executed as concurrently as possible, while some key points need to be stopped or waiting between them. For example, if two threads attempt to access the same global variable at the same time, the result may not be as desired. The
qmutex  provides mutually exclusive locks, or mutex amounts. At most one thread has a mutex at a time, and if a thread tries to access a mutex that is already locked, it will hibernate until the thread that owns the mutex unlocks the mutex. Mutexes is commonly used to protect shared data access. The
qreadwriterlock  is similar to Qmutex, except that it treats "read", "write" access differently. It enables multiple readers to access data in a common time. Using Qreadwritelock instead of Qmutex can make multi-threaded routines more concurrency-capable.

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 protects a certain number of identical resources, and in contrast, 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 scratch. Consumers constantly read data from buffers. The semaphore has better concurrency than the mutex, and if we use mutex to control the access to the buffer, then the producer, the consumer cannot access the buffer at the same time. However, we know that at the same time, different threads have no harm in accessing different parts of the buffer.

const int datasize = 100000;
const int buffersize = 8192;
Char Buffer[buffersize];

Qsemaphore freebytes (buffersize);
Qsemaphore usedbytes;

Class Producer:public Qthread
{
Public
void run ();
};

void Producer::run ()
{
Qsrand (Qtime (0,0,0). Secsto (Qtime::currenttime ()));
for (int i = 0; i < datasize; ++i) {
Freebytes.acquire ();
Buffer[i% buffersize] = "ACGT" [(int) Qrand ()% 4];
Usedbytes.release ();
}
}

Class Consumer:public Qthread
{
Public
void run ();
};

void Consumer::run ()
{
     for (int i = 0; i < datasize; ++i) {
   &nb sp;     Usedbytes.acquire ();
         fprintf (stderr, "%c", buffer[i% buffersize]);
         freebytes.release ();
    }
     fprintf (stderr, "\ n");
}

int main (int argc, char *argv[])
{
Qcoreapplication app (argc, argv);
Producer Producer;
Consumer Consumer;
Producer.start ();
Consumer.start ();
Producer.wait ();
Consumer.wait ();
return 0;
}
Qwaitcondition allows a thread to wake 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 up one, Wakeall () wakes all.

In the following example, the producer must first check whether the buffer is full (numusedbytes==buffersize), and if so, the thread stops to wait for the buffernotfull condition. If not, produce the data in the buffer, increase the numusedbytes, activate the condition buffernotempty. Use a mutex 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. When a 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 the program starts running, only the producer can work. Consumers are blocked waiting for buffernotempty conditions, and once the producer puts a byte in the buffer, the buffernotempty condition is fired and the consumer thread is awakened.

const int datasize = 100000;
const int buffersize = 8192;
Char Buffer[buffersize];

Qwaitcondition Buffernotempty;
Qwaitcondition Buffernotfull;
Qmutex Mutex;
int numusedbytes = 0;

Class Producer:public Qthread
{
Public
void run ();
};

void Producer::run ()
{
Qsrand (Qtime (0,0,0). Secsto (Qtime::currenttime ()));

for (int i = 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:public Qthread
{
Public
void run ();
};

void Consumer::run ()
{
for (int i = 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");
}

int main (int argc, char *argv[])
{
Qcoreapplication app (argc, argv);
Producer Producer;
Consumer Consumer;
Producer.start ();
Consumer.start ();
Producer.wait ();
Consumer.wait ();
return 0;
}

reentrant and thread safe

in QT documentation, the term "reentrant" and "Thread safe" is used to illustrate how a function is used in multithreaded programs. If any function of a class can be called simultaneously by multiple threads on multiple different instances of this class, then this class is called "Reentrant". If different threading functions work on the same instance, it is called "thread-safe".
Most C + + classes are inherently reentrant because they typically refer only to member data. Any thread can call such a member function on one instance of the class, as long as no other thread calls this member function on the same instance. For example, the following Counter class is reentrant:
Class counter
{
Public:
      Counter () {n=0;}
      void Increment () {++n;}
      void Decrement () {--n;}
      int value () const {return n;}
Private:
      int n;
};
This class is not thread-safe because if multiple threads are trying to modify data member N, the result is undefined. This is because the + + and--operators in C + + are not atomic operations. In fact, they will be extended to three machine instructions:
1, load the variable value into register
2, increase or decrease the value in the Register
3, write 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, write back. Their write operations overlap, causing the value of the variable to increase only once. Obviously, the access should be serialized: A should not be interrupted when performing 123 steps. The simplest way to make this class thread safe is to use Qmutex to protect data members:
Class counter
{
Public:
     Counter () {n = 0; }

void Increment () {Qmutexlocker locker (&mutex); ++n;}
void Decrement () {Qmutexlocker locker (&mutex);--n;}
int value () const {Qmutexlocker locker (&mutex); return n;}

Private
mutable Qmutex Mutex;
int n;
};
The Qmutexlocker class automatically locks the mutex in the constructor and unlocks it in a destructor. Incidentally, the mutex is decorated with the mutable keyword because 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 indicate the start and end of thread execution, and also provides many slots. More interestingly, qobjects can be used for multi-threading, because each thread is allowed 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 in multiple threads at the same time. It is important to note that these classes are designed to be created and used in a single thread, so it is not guaranteed to work well if you create an object in one of the threads and invoke its function in another thread. There are three types of constraints to note:
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 another object (because the Qthread object itself is created in another thread)
2, event-driven objects are only used in a single thread. Specifically, this rule applies to "timer mechanism" and "grid module", for example, you should not start a timer in a thread or connect a socket, when this thread is not the thread on which these objects are located.
3, you must ensure that all objects created in the thread are deleted before you delete Qthread. This is 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, put some time-consuming operations into separate worker threads, and when the worker thread is finished, the results are displayed on the screen owned by the main thread.

Per-thread event loop

Each thread can have its event loop, 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.

The event loops in the thread allow the thread to use non-GUI classes (such as qtimer,qtcpsocket,qprocess) that require event loops. You can also connect any thread's signals to a specific thread's slots, which means that the signal-slot mechanism can be used across threads. For objects created before qapplication, Qobject::thread () returns 0, which means that the main thread handles only the posting events for these objects, and does not handle additional events for objects that do not have a thread to which they belong. You can use Qobject::movetothread () to change the thread affinity between it and its children, and if the object has a father, it cannot move the relationship. It is not safe to delete a Qobject object in another thread (not the one that created it). Unless you can guarantee that the object is not handling events at the same time. You can use Qobject::d eletelater (), which will post a Deferreddelete event, which is eventually 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 also applies to the main thread). You can manually use the thread-safe function qcoreapplication::p ostevent (), at any time, to post an event to any object in any thread, and the event is distributed through the event loop in the thread that created the object. The event filter is also supported on all threads, but it 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.

accessing the Qobject subclass from another thread

Qobject and all its subclasses are non-thread-safe. This includes the entire event delivery system. Keep in mind that when you are accessing an object from another thread, the event loop can post events to your Qobject subclass. 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 survives in the thread that created it---not the thread that was created when Qthread::run () was invoked. 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 emission is thread-safe.



QT supports three types of signal-slot connections:
1, directly connected, when the signal is launched, the slot is called immediately. This slot is executed in the thread that launched the signal (not necessarily the one that receives the object's survival)
2, the queue is connected, and the slot is called when control returns to the event loop of the thread to which the object belongs. This slot is executed in the thread that receives the object's survival
3, automatic connection (default), if the signal is emitted with the receiver in the same thread, its behavior as a direct connection, otherwise, its behavior as a queue connection.
The connection type may be specified by passing parameters 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. Similarly, it is 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 simple, and the shared class contains a pointer to the shared data block that contains the actual original data and a reference count. Translates a deep copy into a shallow copy, which improves performance. This mechanism works behind the scenes, and programmers don't need to care about it. If you look deeper, if the object needs to modify the data and the reference count is greater than 1, it should first detach (). So that it does not affect other stakeholders, since the modified data and the original data is different, so it is impossible to share, so it first to perform a deep copy, the data back, and then on this data to modify. For example:
void Qpen::setstyle (Qt::P enstyle style)
{
Detach (); Detach from common data
D->style = style; Set the style member
}

void Qpen::d Etach ()
{
if (d->ref! = 1) {
...//Perform a deep copy
}
}
It is generally believed that implicit sharing is less harmonious than multithreading because there is a reference count. One way to protect the reference count is to use a mutex, but it is slow, and the earlier version of QT does not provide a satisfactory solution. Starting with 4.0, the implied shared class can be safely copied across threads, just like any other value type. 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 access the same object in multiple threads, you also need to serialize the access order with the mutex, just as other reentrant objects are. In general, implicit sharing is really "hidden", and in multithreaded programs, you can think of them as generic, unshared, reentrant types that are safe.

Qt multithreaded Programming

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.