In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article will explain in detail about the WPF threading model and how to use Dispatcher. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.
The WPF threading model starts with two threads of WPF: one for processing rendering and one for managing UI. And discuss the related objects of Dispatcher at the same time.
Start to write this WPF series, the one-stop here is to strive to explain it at every point, of course, not so perfect, if there is something wrong, friends are welcome to correct, I will gradually add, and strive to write this series well.
Typically, WPF applications start with two threads: one for processing rendering and one for managing UI. The rendering thread effectively runs hidden in the background, while the UI thread receives input, handles events, draws the screen, and runs the application code.
The UI thread queues work items within an object named Dispatcher. Dispatcher selects work items based on priority and runs each work item until it is completed. Each UI thread must have at least one Dispatcher, and each Dispatcher can only execute work items in one thread.
These two paragraphs are descriptions of the WPF threading model on MSDN. This paper mainly introduces two concepts: one is divided into two threads in WPF, one is used for rendering (Render), and the other is used to manage UI; II. In UI threads, a class named Dispatcher is used to help UI threads handle tasks.
So what exactly is this threading model and Dispatcher, what are its characteristics, and what are its advantages and disadvantages? Before I formally analyze the threading model and Dispatcher, I'll find an insertion point that I hope will be understood by my friends.
As a base of Presentation, the mission of WPF is to write a graphical interface. On the Windows operating system, the graphical interface is based on the message mechanism, so what steps does it take to create a window?
1. Create a window class. WNDCLASSEX wcex; RegisterClassEx & wcex)
two。 Create a window. CreateWindow (…) ; ShowWindow (...) ; UpdateWindow (...)
3. Set up a message pump.
While (& msg, NULL, 0,0) {TranslateMessage (& msg); DispatchMessage (& msg);}
For example, we produce equipment in an automated factory. Based on formality, we will first define the template for the device, which is to create a window class, where "class" means more about the category. When the template is defined, we can formally produce the device. This is to create the window. The CreateWindow will match the template we defined (window class) through a string. After the establishment is successful, if we want to get the device moving, we must, like people, have a blood-like transmission mechanism in the body to convey commands to all parts of the device. This is the message pump, which, like our heart, continuously distributes blood (messages) through GetMessage and Dispatch. Since we give instructions to the device through messages, there must be a message queue to store messages. In Windows, the thread is the basic scheduling unit, and this message queue is on the thread. When the GetMessage is recycled, the message is uncomplainingly fetched from the message queue of the current thread and distributed to the corresponding window.
So when it comes to WPF, what kind of situation is it, how is it compatible with the old technology, and what are the new breakthroughs?
WPF introduces the concept of Dispatcher, the main function of this Dispatcher is similar to the message queue in Win32, in its internal function, it still calls the traditional operations such as creating window class, creating window, setting up message pump and so on. Dispatcher itself is a singleton pattern, and the constructor is private, exposing a static CurrentDispatcher method used to get the Dispatcher of the current thread. As far as the thread is concerned, it knows nothing about Dispatcher. Dispatcher maintains a static List_dispatchers that traverses this _ dispatchers whenever the CurrentDispatcher method is used. If it is not found, a new Dispatcher object is created and added to _ dispatchers. Dispatcher maintains a property of Thread internally. When creating a Dispatcher, the current thread will be assigned to the attribute of this Thread. The next time you look up, use this field to match whether the Dispatcher of the current thread has been saved in _ dispatchers.
So when is this creation window and message pump called? Inside Dispatcher, a HwndWrapper field is maintained, and in the constructor of Dispatcher, the constructor of HwndWrapper is called, which creates the window class in which the window is created. The actual class here is MessageOnlyHwndWrapper, the Message-Only, which is a common trick in Windows programming to create a hidden window that is only used to dispatch messages. So when was the message pump set up to cycle through the message?
Dispatcher provides a static Run function, as the name implies, is to start Dispatcher, inside the function, called the PushFrame function, in this function, you can find the familiar GetMessage, TranslateAndDispatchMessage. So what's going on with this PushFrame and where does the concept of Frame come from?
This is a new concept introduced by the WPF threading model, the nested message pump, that is, in a While (GetMessage (...)). Another While (GetMessage (...)) is launched internally. Each time PushFrame is called, a new nested message pump is started. Each time GetMessage is called, a message is fetched from the thread's message queue, and the GetMessage does not return False until the WM_QUIT is fetched. The GetMessage function Windows is processed internally to suspend the thread of execution when the message queue is empty to avoid the occurrence of a dead loop. With regard to the pros and cons of the nested message pump, we'll talk about it later. Let's take a look at how Dispatcher handles tasks:
Many Message are defined in Windows, starting with WM_. You need to set the window procedure function when registering the window class, and the messages obtained by GetMessage are redistributed to the window procedure function. The whole process is as follows:
This picture comes from Hou Jie's classic book MFC, 1. First create the Window and specify the window's procedure function WndProc. two。 When the window is created, a WM_CREATE is put into the message queue, 3. The message pump gets the message through GetMessage and distributes it to the window, and the window procedure function processes the WM_CREATE message.
So what role does Dispatcher of the WPF threading model play in this process? When the window procedure function receives the message, it needs to translate the Windows message into internal RoutedEvent or call the layout function according to the type of message. As mentioned earlier, the main function of Dispatcher is similar to the message queue in Win32. The object stored in this queue is DispatcherOperation. This DispatcherOperation, as its name implies, encapsulates each execution item into an object, similar to:
This queue is of type PriorityQueue and is a queue with priority. WPF defines this priority DispatcherPriority, which has
When DeQueue is called on this PriorityQueue, the task with priority of * is fetched. So when were the tasks in this queue added and when were they taken out and executed?
Dispatcher exposes two methods, Invoke and BeginInvoke, which also have several different parameter overloads. BeginInvoke is still called inside Invoke. A typical BeginInvoke parameter is as follows:
Public DispatcherOperation BeginInvoke (Delegate method, DispatcherPriority priority, params object [] args)
Inside this BeginInvoke, the execution function method and the parameter args are encapsulated into DispatcherOperation and added to the PriorityQueue by pressing priority. The return value is the internally created DispatcherOperation. In other words, every time Invoke and BeginInvoke are called, a task is added to the Dispatcher, so when will the task be executed?
DispatcherPriority defines a number of priorities, which WPF divides into two main categories. Foreground priority and background priority, including Loaded~Send in the foreground and Background~Input in the background. The remaining priorities, except Invalid and Inactive, are idle priorities and are processed in the same order as the background priority. The dividing line between foreground priority and background priority is distinguished by Input, where Input refers to keyboard input and mouse movement, click, and so on. The sources of ProrityQueue are:
Of course, the Hwnd-level Hook messages here are finally added to the Dispatcher queue by calling the Invoke/BeginInvoke method of Dispatcher. When processing this PriorityQueue, the priority in the queue is first obtained, and if it belongs to the foreground priority, it is executed. If it is a background priority, it scans the thread's message queue to see if there are Input messages like WM_MOUSEMOVE. If not, execute. If it exists, it discards execution, starts a Timer, and continues to determine whether it can be executed when the Timer is called.
What about the timing of dealing with PriorityQueue? When you call BeginInvoke to add an execution item to the queue, the judgment that handles Queue is also called. The judgment logic is similar to the above. The * priority in the queue is the foreground priority. PostMessage to the hidden window, this message is a custom message registered by Disptcher using RegisterWinodwMessage. Then if the custom message is fetched during GetMessage, the PriorityQueue is processed. If it is a background priority, scan the Input messages of the thread message queue to decide whether to start Timer or PostMessage.
For example, using Invoke to send a request to a UI thread in a background thread goes through the following process:
1. Call Invoke to determine the passed parameter DispatcherPriority. If it is Send, this is a special priority. Switch the thread context directly, execute the task, and return. If it is another priority, call BeginInvoke.
two。 In BeginInvoke, the incoming Delegate and parameters are encapsulated as DispatcherOperation and added to the PriorityQueue.
3. Call the request function handled by the queue and want to process the PriorityQueue.
4. If the * * priority in the queue belongs to the foreground priority, call PostMessage to send a custom message to the hidden window. The background processing is omitted here.
5. Get the message in GetMessage and distribute it to the hidden window, where the common SubWindow (Note 1) is used, and the message is sent to the WndProcHook function of Dispatcher by Hook for processing.
6. In WndProcHook, if the Window message received is a message customized by Dispatcher, the PriorityQueue is actually processed.
7. Process PriorityQueue, take a task from it, determine the priority of the foreground and background, and decide whether to process it or start Timer to process it later.
Coming back, let's talk about nested message loops, starting with modal dialogs. A typical modal dialog scenario is as follows:
SomeCodeA ()
Bool? Result = dlg.ShowDialog ()
SomeCodeB ()
The code runs in the UI thread. When it is executed to dlg.ShowDialog, it starts the modal dialog box and waits for the user to click Yes/No or close the dialog box. After the dialog box closes, the program continues to execute the SomeCodeB code. Then the program waits at SomeCodeB for ShowDialog to return before continuing execution. Of course, you can use WaitHandle to synchronize, but this requires suspending the current (UI) thread, and if there are UI actions such as animation in the main window, there will be no response. Here WPF uses PushFrame, which sets up another message pump inside ShowDialog. While (GetMessage (…)) . On the one hand, you can ensure that messages in the UI thread can be processed; on the other hand, because it is a While loop, returning when the dialog box is closed ensures the order in which the SomeCodeB is executed.
So is this nested message loop really like this? Of course not. It opens one door as well as another. In one scenario, when a WM_SIZE message is received, the Layout system starts processing. If PushFrame is started again during this process, then the nested message pump will continue to fetch the message from the message queue, and if the next message is also WM_SIZE, then process it. Assuming that the nested message pump returns at the end of the message processing, then the * WM_SIZE can continue processing. In this way, an error occurred and the processing order of 12 was changed to 121. Of course, this doesn't just happen in Layout, so WPF adds a DisableProcessing function to Dispatcher, which is called in key procedures such as Layout, stopping pump messages and disabling PushFrame in the process.
In WPF, all WPF objects are derived from DispatcherObject,DispatcherObject. The Dispatcher property is used to get the Dispatcher corresponding to the thread of the creation object. In view of thread affinity, the DispatcherObject object can only be accessed by the thread that created it. Other threads need to get the corresponding Dispatcher to modify the DispatcherObject and call Invoke or BeginInvoke to throw in the task. A UI thread has at least one Dispatcher to set up a message pump processing task, and a Dispatcher can only correspond to one UI thread. What about UI threads and Render threads?
At the beginning, it is mentioned that the WPF thread model is divided into two, one is the UI thread and the other is the Render thread. The two are designed as separate relationships and communicate through Channel (event). The quantitative relationship between the two is that a WPF process can have only one Render thread, and there can be more than or equal to one UI thread. Usually it is a UI thread, that is, a Dispatcher, so when do you need to build multiple threads?
In most cases it is not needed, and in a few cases, such as MediaElement, or Host other ActiveX controls, we expect to create them in other threads to improve performance. You can create a new thread, create a control in the new thread, and call Dispatcher.Run to start Dispatcher. In this way, the main Window and the new control are in different threads, and the communication between the two can be displayed in the main Window using VisualTarget to connect the visual tree or using D3DImage to copy the new control.
It's good to start. WPF doesn't have any new technology, but it puts forward a lot of new concepts.
Note 1: SubWindow, subwindow subclassing. In general, all Window of the same category share the same message handling function WndProc, and the child Window can call SetWindowLong to replace WndProc with SubWndProc, which is often called Sub-Window.
This is the end of the article on "WPF threading model and how to use Dispatcher". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.