Event loop and thread 1

Source: Internet
Author: User
After reading this article for the first time, the translator feels like a spring breeze and deeply understands that the author of the original article has spent a lot of time writing this article, I believe that readers who have carefully read the original text or the following translations will surely reap the harvest.

Due to the long term of the original article, the idea of the original article is gradually extended from the event loop to the discussion of thread usage. Due to time constraints, the translator has temporarily published translations of the event loop. The practical translations of the other half of the thread will be published soon. Please forgive me for any improper translation.

Introduction

Threads are one of the most popular topics in qt channels. Many people have joined the discussion and asked how to solve the problems they encountered when running cross-thread programming.

Quickly review their code. Among the problems found, the biggest problem was that they used threads somewhere, and then fell into the trap of parallel programming. The ease-of-use nature of creation and running threads in Qt, lack of relevant programming knowledge, especially Asynchronous Network programming knowledge, or the habit of using other tool sets. These factors are mixed with the Qt signal slot architecture. together, people often let themselves fall under their feet. In addition, Qt's support for threads is a double-edged sword: even if it is easy for you to implement Qt multi-threaded programming, you must be especially careful when adding many new features to Qt, in particular, QObject interaction.

Purpose of this ArticleNoIt teaches you how to use threads, how to properly lock, or how to perform parallel development or how to write scalable programs. There are many good books on these topics, for example, the list of recommended books provided by this link. this article mainly aims to introduce the event loop and thread usage of Qt 4 to readers, and aims to help readers develop multi-threaded code with better structure and robustness, avoid Qt event loops and Common Errors in thread usage.

Prerequisites

Considering that this article is not a general introduction to thread programming, we hope you have the following knowledge:

  • C ++ basics;
  • Qt basics: QOjbects, signal/slot, and event processing;
  • Understand what is the thread, the relationship between the thread and the process, and the operating system;
  • Understand how mainstream operating systems start, stop, wait, and end a thread;
  • Learn how to use mutexes, semaphores, and wait conditions to create a thread-safe/reentrant function, data structure, and class.

In this article, we will follow the following Glossary:

  • ReentrantA class is called reentrant: As long as at most one thread accesses the same instance at the same time, we can say that multiple threads can safely use their own instances in their respective threads. A function is called reentrant: if each callback function only accesses its unique data (Note: global variables are not unique but shared ), so we can say that multiple threads can call this function safely. That is to say, users of classes and functions must use some external locking mechanisms to access object instances or serialize shared data.
  • Thread SecurityIf multiple threads can use a class object at the same time, this class is called thread-safe. If multiple threads can use the shared data in one function body at the same time, this function is called thread-safe.

Note: more reentrant and t thread-safe explanations: for classes, if all its member functions can be called by different threads at the same time without affecting each other-even if these calls are for the same class object, the class is defined as thread security. For classes, if different instances can be used simultaneously in different threads without affecting each other, the classes are defined as reentrant. In the definition of Qt, at the class level, thread-safe is more stringent than reentrant)

Events and event Loops

As an event-driven tool set, Qt plays a core role in event and event dispatching. This article will not fully discuss this topic, but will focus on some key concepts related to threads. For more information about the Qt Event System, see (here[Doc.qt.nokia.com]And here
[Doc.qt.nokia.com]) (Translator's note: You are also welcome to refer to the blog post written by the translator: On the event processing mechanism of Qt 1 and 2)

A Qt event represents an object that another person is interested in and has occurred. The main difference between an event and a signal is that, events are targeted at a specific target object in our application (which determines how we handle this event), while signal transmission is "aimlessly ". From the code perspective, all event instances are QEvent[Doc.qt.nokia.com]And all the derived classes of QObject can overload the virtual function QObject: event () to process the events of the target object instance.

Events can be generated inside the application or from outside. For example:

  • The QKeyEvent and QMouseEvent objects represent interaction events related to the keyboard and mouse. They come from the Windows Management Program.
  • When the timer starts timing, the QTimerEvent object is sent to the QObject object, which often comes from the operating system.
  • When a subclass object is added or deleted, the QChildEvent object will be sent to a QObject object, and they are from within your application.

An important thing for events is that they are not immediately distributed when an event is generated, but are included inEvent queue(Event queue. The dispatcher traverses the event queue and sends the events in the stack to their target objects. Therefore, they are calledEvent loop). In terms of concept, the code below describes the outline of an event loop:

[cpp] view plaincopyprint?
 
 
  1. 1: while (is_active)
  2. 2: {
  3. 3: while (!event_queue_is_empty)
  4. 4: dispatch_next_event();
  5. 5:
  6. 6: wait_for_more_events();
  7. 7: }
1: while (is_active) 2: { 3: while (!event_queue_is_empty) 4: dispatch_next_event(); 5: 6: wait_for_more_events(); 7: }

We enter the main event loop of Qt by running QCoreApplication: exec (). This will cause blocking until QCoreApplication: exit () or QCoreApplication: quit () is called, then, the cycle ends.

This "wait_for_more_events ()" function is blocked until an event is generated. If we think about it, we will find that all entities that generate events at that time must come from external resources (because the distribution of all internal events has ended, there are no pending events in the event queue, so the event loop is awakened as follows:

  • Windows Management Activities (Keyboard buttons, mouse clicks, interaction with windows, etc );
  • Socket activity (there are visible data for reading or a writable non-blocking Socket, a new Socket connection );
  • Timers (timer starts timing)
  • Post events of other threads (see the following article ).

In Unix systems, window management activities (X11) Notify applications (events are generated) through socket (UNIX domain or TCP/IP) because clients use them to communicate with X servers. If we decide to use an internal socketpair (2) for cross-thread event distribution, what windows management activity needs to wake up is

  • Sockets;
  • Timers;

This is alsoSelect (2)What the system calls: It monitors a set of descriptors for window management activities. If there is no activity within a period of time, it times out. What QT needs to do is to convert the return value of the system call select to the correct qevent subclass object and include it in the stack of the event queue, now you know what is contained in the event loop :)

Why do we need to run an event loop?

The following list is not complete, but you will have a panorama. You should be able to guess which classes need to use the event loop.

  • WidgetsDrawing and Interaction: When a QPaintEvent event is dispatched, QWidget: paintEvent () will be called. QPaintEvent can generate internal QWidget: update (), or external window management (for example, a display hidden window ). Similarly, events corresponding to various interactions (such as the keyboard and mouse) must be distributed cyclically.
  • Timers: In short, when a select (2) or similar call times out, the timer starts timing. Therefore, Qt needs to let those calls work for you by returning an event loop.
  • Networking: The underlying Qt network classes (QTcpSocket, QUdpSocket, QTcpServer, etc.) are all designed to be asynchronous. When you call read (), they only return Visible data. When you call write (), they only include write operations in the execution schedule for later execution. Real read/write only happens when the event loop returns. Although the Qt network class provides the corresponding synchronization method (waitFor * family), they are not recommended because they block the waiting event loop. Provides upper-layer classes such as QNetworkAccessManager without synchronous APIs.
    Event loops are also required.

Blocking event Loops

Why?Never block the event LoopPreviously, let's try to further understand what "blocking" means. Suppose you have a Button widget, which will emit a signal when it is pressed; another worker object defined below connects this signal, in addition, this object slot does a lot of time-consuming things. After you click this button, the function call stack below is shown below:

[CPP]View plaincopyprint?
  1. Main (int, char **)
  2. QApplication: exec ()
  3. [...]
  4. QWidget: event (QEvent *)
  5. Button: mousePressEvent (QMouseEvent *)
  6. Button: clicked ()
  7. [...]
  8. Worker: doWork ()

Main (int, char **) <br/> QApplication: exec () <br/> [...] <br/> QWidget: event (QEvent *) <br/> Button: mousePressEvent (QMouseEvent *) <br/> Button: clicked () <br/> [...] <br/> Worker: doWork ()

In Main (), we enable the event loop by calling qapplication: exec () (as shown in line 2nd of the code above. The window manager sends a mouse click event, which is captured by the QT kernel and converted to a qmouseevent. Then, qapplication: Notify () (notify is not displayed in the above Code) the event () method sent to our widget (4th rows ). Because the button does not overload event (), its base class qwidget method can be called.
QWidget: event () detects that an input event is a mouse click and calls its proprietary event processor, that is, Button: mousePressEvent () (5th rows ). We reload the mousePressEvent method and transmit the Button: clicked () signal (line 1). This signal activates the very time-consuming worker: doWork () in our Worker object () slot (8th rows ). (Translator's note: If you describe more details about the Function stack described in this section, refer to section 1 and II of Qt's event processing mechanism)

When a worker object is busy, what is the event loop doing? You may have guessed the answer: nothing is done! It distributes mouse click events and is blocked when the event handler returns. WeBlocking event loops,That is to say, no events will be distributed or pending events will be handled before our doWork () slot (Row 3) completes the work.

When event dispatching gets stuck,Widgets will not refresh itself (The QPaintEvent object will wait in the event queue ),Nor can any further interaction with widgets occur.The timer will not start timing,Network Communication will become slow and stagnant. More seriously, many windows management programs will detect that your applications no longer process eventsTell the user that your program no longer responds (not
Responding)
This is why it is so important to quickly respond to events and return event loops as quickly as possible.

Force Event Loop

So what should we do for tasks that require a long time to run without blocking the event loop? One feasible answer is to move this task to another thread: In this section, we will see if we do it. One possible solution is to manually force the event to run cyclically in our blocked tasks by calling QCoreApplication: processEvents. QCoreApplication: processEvents () processes all events in the event queue and returns them to the caller.

Another optional force-reimport event solution is to use QEventLoop.[Doc.qt.nokia.com]Class. By calling QEventLoop: exec (), we re-import the event loop, and we can connect the signal to the QEventLoop: quit () slot to exit the event loop, the following code is used:

[cpp] view plaincopyprint?
 
 
  1. 1: QNetworkAccessManager qnam;
  2. 2: QNetworkReply *reply = qnam.get(QNetworkRequest(QUrl(...)));
  3. 3: QEventLoop loop;
  4. 4: QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
  5. 5: loop.exec();
  6. 6: /* reply has finished, use it */
1: QNetworkAccessManager qnam; 2: QNetworkReply *reply = qnam.get(QNetworkRequest(QUrl(...))); 3: QEventLoop loop; 4: QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); 5: loop.exec(); 6: /* reply has finished, use it */

QNetworkReply does not provide a blocking API, and it requires an event loop to be run. We enter a local QEventLoop, and when the response is complete, the partial event loop exits.

When the Re-Entry Event loop is completed from another path, be very careful: it may lead to endless recursive loops! Let's go back to the Button example. If we call QCoreApplication: processEvents () in the doWork () slot, and the user clicks the button again, the doWork () slot willAgainCalled:

[Cpp]View plaincopyprint?
  1. Main (int, char **)
  2. QApplication: exec ()
  3. [...]
  4. QWidget: event (QEvent *)
  5. Button: mousePressEvent (QMouseEvent *)
  6. Button: clicked ()
  7. [...]
  8. Worker: doWork ()
    // Implementation, internal call
  9. QCoreApplication: processEvents () // we dispatch events manually and...
  10. [...]
  11. QWidget: event (QEvent *) // another mouse click event is sent to the Button
  12. Button: mousePressEvent (QMouseEvent *)
  13. Button: clicked ()
    // Here again emit clicked ()...
  14. [...]
  15. Worker: doWork ()
    // Finished! We have recursively called the doWork slot.

Main (int, char **) <br/> QApplication: exec () <br/> [...] <br/> QWidget: event (QEvent *) <br/> Button: mousePressEvent (QMouseEvent *) <br/> Button: clicked () <br/> [...] <br/> Worker: doWork () // implementation, internal call <br/> QCoreApplication: processEvents () // we dispatch events manually and... <Br/> [...] <br/> QWidget: event (QEvent *) // another mouse click event is sent to the Button <br/> Button: mousePressEvent (QMouseEvent *) <br/> Button:: clicked () // here again emit has clicked ()... <Br/> [...] <br/> Worker: doWork () // finished! We have recursively called the doWork slot.

A quick and simple temporary solution is to pass QEventLoop: ExcludeUserInputEvents to QCoreApplication: processEvents (), that is, tells the event loop not to dispatch any user input events (events will simply stay in the queue ).

Similarly, use deleteLater () of an object to Implement AsynchronousDelete event(Or, it may lead to any "shutdown" event), be alert to the impact of the event loop. (Note: deleteLater () will delete the object in the event loop and return it)

[Cpp]View plaincopyprint?
 
 
  1. 1: QObject * object = new QObject;
  2. 2: object-> deleteLater ();
  3. 3: QEventLoop loop;
  4. 4: loop.exe c ();
  5. 5:/* Now the object is a wild pointer! */
1: QObject * object = new QObject; 2: object-> deleteLater (); 3: QEventLoop loop; 4: loop.exe c (); 5: /* Now the object is a wild pointer! */

We can see that QCoreApplication: processEvents () is not used (events deleted after Qt 4.3 are no longer distributed ), however, we do use other partial event loops (such as the loop started by QEventLoop, or the QDialog: exec () to be introduced below ()).

Remember when we callQDialog: exec ()Or QMenu: exec (), Qt enters a local event loop. In Versions later than Qt 4.5, QDialog provides the QDialog: open () method to display the window-modal dialog box without entering a local loop.

[Cpp]View plaincopyprint?
 
 
  1. 1: QObject * object = new QObject;
  2. 2: object-> deleteLater ();
  3. 3: QDialog dialog;
  4. 4: dialog.exe c ();
  5. 5:/* Now the object is a wild pointer! */
1: QObject * object = new QObject; 2: object-> deleteLater (); 3: QDialog dialog; 4: dialog.exe c (); 5: /* Now the object is a wild pointer! */

Now the discussion of event loop has come to an end. Next, we will discuss the multi-thread of Qt: event loop and thread 2.

Respect Original Works and translations. Reprinted, please maintain the integrity of the article, and in the form of a hyperlink to indicate the original author's main site address, so that other friends can ask questions and correct.


From: http://blog.csdn.net/changsheng230/article/details/6101232

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.