WPF learning and sharing II: dispatcherobject and WPF thread model (II)

Source: Internet
Author: User

In the previous article, we analyzed how to compile GUI applications using Win32 and winform.ProgramMain problems. To sum up, the most important thing is how to efficiently update the interface from the worker thread. So let's first look at how WPF achieves this goal.

Use of dispatcherobject

The dispatcherobject class has two member Methods: checkaccess and verifyaccess. The checkaccess function is the same as the control. invokerequired attribute in winform. If the call thread and the object creation thread are not the same instance, false is returned. Verifyaccess throws an exception.

The dispatcherobject also has a dispatcher attribute, which returns the creation dispatcher of the control. Through its invoke method, we can update the interface operation marshal to the control creation thread. For example:

1 Textbox =   New Textbox ();
2 If ( ! Textbox. checkaccess)
3 {
4 Textbox. Dispatcher. Invoke (dispatcherpriority. Send, Delegate {});
5 }

The implementation method of checkaccess () is similar to the last example in the previous article. By comparing two thread instances, the speed is very fast and only a few il commands are required. Classes that inherit dispatcherobject call verifyaccess internally for judgment. Therefore, an exception is thrown when a thread is not created. The advantage is that we can discover it earlier.CodeNot as uncertain as winform does.

Now let's take a look at the dispatcher class that is actually doing things. Before that, let's review a Win32 message loop.

Win32 message loop

In Win32, the message pump is created by calling getmessage cyclically. When a message of the wm_quit type is received, it exits. However, changes to window or control properties, such as color and position, are actually called postmessage or sendmessage. When the call thread and the owner thread of the window are the same, sendmessage directly calls the window function. If different, the message is placed in the message queue corresponding to the window, which is retrieved and processed by getmessage. Winform is actually based on Win32, so when we write lable. Text = "Hello World", we actually call methods such as postmessage.

The following describes the features and restrictions of Win32 message queue:

    1. Take out messages from the message queue and call the corresponding window processing function.
    2. After a message is sent to the queue, it cannot be controlled.
    3. Message Processing supports a limited priority, but all of them are controlled by the OS. User code cannot control or change the priority.
    4. Application. doevents is returned only after all messages in the message queue are processed. Of course, Win32 does not have this restriction, because message pump is created by the user.
    5. You can add hooks to secretly do some bad things.

The main function of Dispatcher in WPF is similar to the message queue in Win32. Instead of replacing the message loop, it is built on the message loop. First, let's look at the structure of dispatcher:

Thread-related dispatcher Creation

When we create a WPF object for the first time in a thread, the dispatcherobject constructor calls dispatcher. currentdispatcher to assign a value to the private Variable _ dispatcher. Dispatcher. currentdispatcher finds the dispatcher instance corresponding to this thread through thread. currentthread. If it does not exist, create one. The dispatcher constructor first creates a priority queue priorityqueue with 11 levels of priority (dispatcherpriority), which is used to save operations with different priorities. Then, call the Win32 API registerclassex registration window handler and call createjavaswex to create an invisible window.

Dispatcher has two static methods to establish a message loop, which are defined as follows:

1 Public   Sealed   Class Dispatcher
2 {
3 Public   Static   Void Run ()
4 {
5 Pushframe ( New Dispatcherframe ());
6 }
7 Public   Static   Void Pushframe (dispatcherframe frame ){...}
8 }

Although it is a static method, it is actually a method that calls the dispatcher instance corresponding to the current thread. Application. Run () actually calls dispatcher. Run () internally, and then calls dispatcher. pushframe (...). When we create a WPF project, vs automatically generates app. XAML, And the compiled code is

1 Public   Static   Void Main ()
2 {
3 Wpfhello. app =   New Wpfhello. app ();
4 App. initializecomponent ();
5 App. Run ();
6 }

After pushframe () or run () is called, the Win32 getmessage method is called cyclically to establish a message loop. In the following example, a new thread is created in the default vs WPF project, a new WPF window is created in the thread, and a message loop is started:

1 Public   Partial   Class APP: Application
2 {
3 Protected   Override   Void Onstartup (startupeventargs E)
4 {
5 // Create a new thread
6 Thread monitorthread =   New Thread (queuemonitor. threadmain );
7 Monitorthread. setapartmentstate (apartmentstate. Sta );
8 Monitorthread. Start ( This );
9
10 Base . Onstartup (E );
11 }
12 }
13
14 Class Queuemonitor
15 {
16 Public   Static   Void Threadmain (Object OBJ)
17 {
18 Queuemonitor Monitor =   New Queuemonitor (OBJ As Application );
19 }
20
21 Private Application m_target;
22 Private Dispatcherframe m_frame;
23 Private Monitorwindow m_window;
24 Public Queuemonitor (Application target)
25 {
26 M_target = Target;
27
28 M_frame =   New Dispatcherframe ();
29 M_window =   New Monitorwindow ();
30
31 M_window.closed + = Monitorwindow_closed;
32 M_{{visibility = Visibility. visible;
33
34 Dispatcher. Run ();
35 // Dispatcher. pushframe (m_frame );
36 }
37
38 Void Monitorwindow_closed ( Object Sender, eventargs E)
39 {
40 Dispatcher. exitallframes ();
41 // M_frame.continue = false;
42 }
43 } Message Loop Control

In the previous example, we can also use the pushframe method. Compared with run (), we can use the pushframe parameter dispatcherframe object to control when to exit the message loop. To exit the cycle of the current layer, you only need to assign the value of dispatcherframe. Continue to false. We can call pushframe nested, which is similar to application. doevents. It is more flexible, but we still cannot just process messages with a specific priority.

In addition, you can call the dispatcher. exitallframes method to exit all message loops of the current thread. Dispatcher also provides the disableprocessing method, which can suspend message loop processing. The following describes how to use this method:

1 Using (Dispatcher. disableprocessing ())
2 {
3 Dispatcher. currentdispatcher. Invoke (dispatcherpriority. Render, Delegate {});
4 }

The dispatcherprocessingdisabled object returned by this method implements the idisposable interface to restore the message loop in dispose. This method can also be nested.

Next, let's take a look at how to deliver messages and how to handle dispatcher.

Dispatcher priority queue

The invoke and begininvoke methods of dispatcher are used to place tasks in the dispatcher priority queue. The former is the synchronous method, and the latter is asynchronous. The second parameter is a delegate object that represents the task to be executed. The first parameter defines the priority of a task. For details, refer to the dispatcherpriority Enumeration type. In general, we can divide it into two categories: Background and foreground, and dispatcherpriority. Send.

When sending is specified in invoke and checkaccess is true, invoke directly calls delegate to execute the task. If not, call begininvoke and wait for the result or time out.

During begininvoke execution, the task is first encapsulated into a dispatcheroperation object and placed in the corresponding priority queue. Then determine whether it is background or foreground. If it is foreground, call postmessage to deliver a message to the Win32 message queue and then return it. If it is a background, check whether there is a timer. If not, create a timer. timer will continuously send messages to Win32 message queues to trigger message processing. If a foreground message is sent, the timer is deleted. In this way, the system can process the background message when it is idle.

When a Win32 message is delivered to a Win32 queue, the registered window function is executed, and a dispatcheroperation is retrieved from the priority queue for execution. After completion, a new Win32 message is delivered to trigger the next execution or wait for the timer message.

The return value of begininvoke is the dispatcheroperation object. With begininvoke, we can cancel, wait, or adjust the priority of the task. In the subsequent series, we will see how to use different foreground priorities.

Priority queue hook

Similar to Win32, we can also add hooks for message processing, and add the following event handler:

1 Dispatcher. currentdispatcher. Hooks. operationaborted + =   New Dispatcherhookeventhandler (hooks_operationaborted );
2 Dispatcher. currentdispatcher. Hooks. operationcompleted + = ;
3 Dispatcher. currentdispatcher. Hooks. operationposted + = ;
4 Dispatcher. currentdispatcher. Hooks. operationprioritychanged + =

In the event handler parameter, we can obtain the corresponding dispatcher and dispatcheroperation objects. In this way, we can filter, query, or change the task priority.

Dispatchertimer

Dispatchertimer is similar to timer in winform. In the constructor, we can specify the priority, dispatcher instance, and so on.

Summary

In general, compared with Win32, If we rate the thread and message Loop Mechanism in WPF, I think we can score a 4-point high. WPF solves the problem of no priority in Win32, cross-thread calling performance, and friendly programming interface. If it is not enough, pushframe can support priority and may have better control over reentrancy. In addition, dispatcheroperation cannot obtain a name. For example, it is inconvenient to develop a queue monitoring program. In my opinion, I can deduct 0.5 points. What is the other 0.5 points?

Finally, if we look at these classes, they are all in windowsbase. dll, that is, the classes in system. Windows. Threading, such as dispatcherobject and dispatcher, can also be used in other systems. We can even use this in our system.

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.