Qt multi-thread programming Summary (1)

Source: Internet
Author: User

Qt provides support for threads, the basic form includes platform-independent thread classes, thread-safe event transmission, and a global Qt library mutex, allowing you to call the Qt method from different threads.

This document is provided to audience who have rich knowledge and experience in multi-threaded programming. Recommended reading:

  • Threads Primer: A Guide to Multithreaded Programming
  • Thread Time: The Multithreaded Programming Guide
  • Pthreads Programming: a posix Standard for Better Multiprocessing (O 'Reilly Nutshell)
  • Win32 Multithreaded Programming

Warning:All GUI classes (such as QWidget and its sub-classes), operating system core classes (such as QProcess), and network classes areNoIs thread-safe.

QRegExp uses a static cache and is not thread-safe, even if the QRegExp object is protected by using QMutex.

Thread

The most important class is QThread. That is to say, to start a new thread, it is to start executing the re-implemented QThread: run (). This is similar to the Java Thread class.

To write a thread program, it is necessary to protect the data when two threads want to access the same data at the same time. Therefore, there is also a QMutex class. A thread can lock the mutex, and after it is locked, other threads can no longer lock the mutex, all threads trying to do this will be blocked until the mutex is released. For example:

    class MyClass    {    public:        void doStuff( int );
    private:
        QMutex mutex;
        int a;
Int B ;}; // Set a to c and B to c * 2.
    void MyClass::doStuff( int c )    {
        mutex.lock();
 a = c;
 b = c * 2;
mutex.unlock();    } 

This ensures that only one thread can enter MyClass: doStuff () at the same time, soBWill always be equalC * 2.

Another thread also needs to wait for other threads to wake up under a given condition, and the QWaitCondition class will be provided. The condition QWaitCondition that the thread waits for indicates what happened, And the blocking will continue until such a thing occurs. When something happens, QWaitCondition can wake up one or all threads waiting for this event. (This is the same as POSIX thread condition variables and is also an implementation of Unix .) For example:

    #include <qapplication.h>
    #include <qpushbutton.h>
// Global condition variable
QWaitCondition mycond; // Worker class implementation
  class Worker : public QPushButton, public QThread
    {        Q_OBJECT
 public:
        Worker(QWidget *parent = 0, const char *name = 0)
            : QPushButton(parent, name)
        {
            setText("Start Working");
// Connect the signal inherited from QPushButton and our slotClicked () method
            connect(this, SIGNAL(clicked()), SLOT(slotClicked()));
// Call the start () method inherited from QThread ...... This will immediately start thread execution
            QThread::start();
        }
public slots:
        void slotClicked()
{// Wake up a thread waiting for this condition variable
            mycond.wakeOne();
        }
protected:
        void run()
        {
// This method will be called by the newly created thread ......
            while ( TRUE ) {
// Lock the application mutex lock and set the window title to indicate that we are waiting to start working.
                qApp->lock();
                setCaption( "Waiting" );
                qApp->unlock();
// Wait until we are notified that we can continue
                mycond.wait();
// If we get here, we have been awakened by another thread ...... Let's set the title to indicate that we are working.
                qApp->lock();
                setCaption( "Working!" );
                qApp->unlock();
// This may take some time, seconds, minutes, or hours. Because the GUI thread is separated from the GUI thread, the GUI thread will not stop when processing the event ......
                do_complicated_thing();
            }
        }
    };
// Main thread-all GUI events are handled by this thread.
    int main( int argc, char **argv )
    {
        QApplication app( argc, argv );
// Create a worker ...... When we do this, the worker will run in a thread
        Worker firstworker( 0, "worker" );
        app.setMainWidget( &worker );
        worker.show();
        return app.exec();
    }
  

As long as you press the button, this program will wake up the worker thread, this thread will perform and do some work, and then will come back to continue waiting for being told to do more work. If the worker thread is working when the button is pressed, nothing will happen. When the thread completes the work and calls QWaitCondition: wait () again, it will be started.

Thread-safe event Transmission

In Qt, a thread is always an event thread-indeed, the thread pulls events from the window system and distributes them to the window parts. Static Method QThread: postEvent transmits an event from the thread, which is different from the event thread. The event thread is awakened and the event is distributed in the event thread as a normal window system event. For example, you can force a widget to redraw through a different thread as follows:

    QWidget *mywidget;
    QThread::postEvent( mywidget, new QPaintEvent( QRect(0, 0, 100, 100) ) );
  

This (asynchronously) causes mywidget to redraw A Square area of 100*100.

Qt library mutex

The Qt library mutex provides a way to call the Qt method from a thread rather than an event thread. For example:

  QApplication *qApp;
  QWidget *mywidget;
 
  qApp->lock();
  mywidget->setGeometry(0,0,100,100);
  QPainter p;  p.begin(mywidget);  p.drawLine(0,0,100,100);  p.end();  qApp->unlock();  

The result of calling a function without mutex in Qt is usually unpredictable. To call a GUI-related function of Qt from another thread, the Qt library mutex is required. In this case, all images or window system resources that may eventually be accessed are GUI-related. The container class, string, or input/output class is used. If an object is used by only one thread, no mutex is required.

Warning

Some things to be aware of during thread programming:

  • Do not perform any blocking operations when using the Qt library mutex. This will freeze the event loop.

  • Make sure that the number of times you lock a recursive QMutex is the same as the number of times you unlock it.

  • Lock the Qt application mutex before calling anything except the Qt container and tool class.

  • Beware of implicitly sharing classes. You should avoid using the operator = () between threads to copy them. This will be improved in major or minor release versions of Qt in the future.

  • Beware of Qt classes that are not designed to be thread-safe. For example, QPtrList application interfaces are NOT thread-safe. If different threads need to traverse a QPtrList, they should call QPtrList:: locked before () and unlocked after reaching the end, instead of locking and unlocking before and after QPtrList: next.

  • Confirm that only the objects created in the GUI thread inherit and use QWidget, QTimer, and QSocketNotifier. On Some platforms, creating such an object in a thread that is not a GUI thread will never accept events in the underlying window system.

  • Similar to the above, QNetwork class is used only in GUI threads. A frequently asked question is whether a QSocket can be used in multiple threads. This is not mandatory because all QNetwork classes are asynchronous.

  • Do not try to call the processEvents () function in a thread that is not a GUI thread. This also includes QDialog: exec (), QPopupMenu: exec (), QApplication: processEvents (), and others.

  • In your applications, do not mix common Qt libraries with Qt libraries that support threads. This means that if your program uses the Qt library that supports threads, you should not connect to a common Qt library, dynamically load a common Qt library, or dynamically connect to other libraries or plug-ins that depend on the common Qt library. In some systems, this will cause the static data used in the Qt library to become unreliable.

QT provides support for threads in three forms. They are platform-independent threads, thread-safe event delivery, and cross-thread signal-slot connections. This makes it easier to develop lightweight multi-threaded Qt programs and make full use of the advantages of multi-processor machines. Multi-threaded programming is also a useful mode, which is used to solve the problem of long-time operations without the user interface losing response.
Qt Thread class
Qt contains the following thread-related classes:
QThread provides a method to start a new thread.
QThreadStorage provides thread-by-thread data storage
QMutex provides mutually exclusive locks or mutex
QMutexLocker is a convenient class that automatically locks and unlocks QMutex.
QReadWriteLock provides a lock for simultaneous read/write operations.
QReadLocker and QWriteLocker are convenient classes that automatically lock and unlock QReadWriteLock
QSemaphore provides an integer semaphore, which is a generalization of mutex.
QWaitCondition provides a method to enable the thread to sleep until it is awakened by another thread.

Qt advanced Thread class
QtConcurrent enables thread transactions
QFutureWatcher observes the thread status
QFuture thread startup class
QThread creation thread
To create a thread, subclass QThread and rewrite its run () function, for example:
Class MyThread: public QThread
{
Q_OBJECT
Protected:
Void run ();
};
Void MyThread: run ()
{
...
}

Call start and Qt to create a thread and run the code in the run () function in the thread. Pay attention to the non-thread security of the UI.

QtConcurrent creation thread
QtConcurrent has many methods to create threads, and QtConcurrent itself is quite special. If the system has Idle threads, It schedules Idle threads and creates a thread when there is no idle thread.
(Note: The creation thread of QtConcurrent is managed by QthreadPool. If the maximum number of threads is exceeded, the thread will be waiting in the queue.) There are multiple methods for creating the thread of QtConcurrent. The following example shows the map function:
QImage scale (const QImage & image)
{
QDebug () <"Scaling image in thread" <QThread: currentThread ();
Return image. scaled (QSize (100,100), Qt: IgnoreAspectRatio, Qt: SmoothTransformation );
}
 
Int main (int argc, char * argv [])
{
QApplication app (argc, argv );
 
Const int imageCount = 20;
 
// Create a list containing imageCount images.
QList images;
For (int I = 0; I <imageCount; ++ I)
Images. append (QImage (1600,120 0, QImage: Format_ARGB32_Premultiplied ));
 
// Use QtConcurrentBlocking: mapped to apply the scale function to all
// Images in the list.
QList thumbnails = QtConcurrent: blockingMapped (images, scale );
 
Return 0;
}

Qt Thread Synchronization
QMutex, QReadWriteLock, QSemaphore, and QWaitCondition provide thread synchronization methods. The main idea of using threads is to expect them to be executed concurrently as much as possible, and the threads must be stopped or waited at some key points. For example, if two threads attempt to access the same global variable at the same time, the result may not be as expected.

QMutex
QMutex provides mutually exclusive locks or mutex volumes. At most one thread has mutex at a time. If a thread tries to access a locked mutex, it will sleep until the thread that owns mutex unlocks the mutex. Mutexes is often used to protect shared data access.
QReadWriterLock
QReadWriterLock is similar to QMutex except that it treats "read" and "write" access differently. It allows multiple readers to access data in a shared manner. Using QReadWriteLock instead of QMutex can make multi-threaded programs more concurrent.
QReadWriteLock lock;
Void ReaderThread: run ()
{
Lock. lockForRead ();
Read_file ();
Lock. unlock ();
}
Void WriterThread: run ()
{
Lock. lockForWrite ();
Write_file ();
Lock. unlock ();
}

QSemaphore
QSemaphore is a generalization of QMutex, which can protect a certain number of identical resources. In contrast, a mutex only protects one resource. In the following example, QSemaphore is used to control the access to the ring-shaped slow speed. The buffer zone is shared by the producer thread and consumer thread. The producer constantly writes data to the buffer until the buffer end and starts from the beginning. The consumer constantly reads data from the buffer. Semaphores have better concurrency than mutex. If we use mutex to control access to the buffer, the producer and consumer cannot access the buffer at the same time. However, we know that at the same time, different threads access different parts of the buffer.
Hazards.
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 ){
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
QWaitCondition allows a thread to wake up another thread in some cases. One or more threads can block waiting for a QWaitCondition and set a condition with wakeOne () or wakeAll. WakeOne () Wake up at random, and wakeAll () Wake up all.
In the following example, the producer must first check whether the buffer is full (numUsedBytes = BufferSize). If yes, the thread stops waiting for the bufferNotFull condition. If not, add numUsedBytes to produce data in the buffer and activate the condition bufferNotEmpty. Use mutex to protect access to numUsedBytes. In addition, QWaitCondition: wait () receives a mutex as the parameter. This mutex should be initialized as locked by the calling thread. Mutex will be unlocked before the thread enters the sleep state. When the thread is awakened
The mutex is locked, and the transition from the locked state to the waiting state is an atomic operation, which prevents competition conditions. When the program starts running, only the producer can work. The consumer is blocked and waits for the bufferNotEmpty condition. Once the producer puts a byte in the buffer, the bufferNotEmpty condition is stimulated, 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;
}

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.