First, create a winform application.ProgramTo add a multi-line text box and a button control. The button event is as follows:
Thread. Sleep (1000 );StringbuilderSB =NewStringbuilder();For(IntI = 0; I <10000; I ++) sb. append ("Test");StringS = sb. tostring (); textbox1.text = s;
First, we can understand this operation as a very time-consuming operation, which takes at least 1 second. After one second, we set a large string as the value of the text box, and then displayed the tag to assign the value to the text box the time required for UI rendering. The program execution result is as follows:
We can feel that after clicking the button, the UI of the entire program gets stuck, and there is no way to change the size after dragging, which is very poor for experience. We generally want to create a new thread to wrap this method so that the UI thread is not stuck:
NewThread() => {Thread. Sleep (1000 );StringbuilderSB =NewStringbuilder();For(IntI = 0; I <10000; I ++) sb. append ("Test");StringS = sb. tostring (); textbox1.text = s;}). Start ();
If you run the program in debug mode, the following exception is returned (not in debug mode ):
Although we know this setting:
Control. Checkforillegalcrossthreadcils =False;
This error can be blocked. However, updating the control status in a thread that does not create a control may cause many problems, such as deadlocks and updating the control. Microsoft recommends that you use the control invoke or begininvoke method to allow the UI thread to perform operations involving control status updates:
NewThread() => {Invoke (NewAction() => {Thread. Sleep (1000 );StringbuilderSB =NewStringbuilder();For(IntI = 0; I <10000; I ++) sb. append ("Test");StringS = sb. tostring (); textbox1.text = s ;}). Start ();
You may think of writing this, but after running the program, you can find that the interface is still stuck. Think about it. Although we opened a new thread, we immediately put the wholeCodeThe section is handed over to the UI thread, but of course it won't work. In fact, the work of this method can be divided into two parts: one is the calculation of our data, and the other is to display the computed data on the interface, we should only put operations related to the real and UI into invoke for the UI thread to do:
NewThread() => {Thread. Sleep (1000 );StringbuilderSB =NewStringbuilder();For(IntI = 0; I <10000; I ++) sb. append ("Test");StringS = sb. tostring (); invoke (NewAction() =>{ Textbox1.text = s ;}). Start ();
After another test, we can find that the UI was not stuck for more than one second, and it was stuck at the last point. Before proceeding to the study of the stuck issue, we can see that control provides the invokerequired attribute to determine whether the current thread is a UI thread or whether the current operation needs to be invoke:
Textbox1.text =This. Invokerequired. tostring ();NewThread() => {Textbox1.text + =Environment. Newline +This. Invokerequired. tostring (); invoke (NewAction() => {Textbox1.text + =Environment. Newline +This. Invokerequired. tostring () ;}). Start ();
You can get the following results by starting the program in non-debug mode:
Obviously:
1) Value assignment outside the thread does not require invoke (in the UI thread)
2) value assignment within the thread requires invoke (not in the UI thread)
3) The value assignment in invoke has been sent to the UI thread, so no invoke is required.
If you continue to study the stuck issue, you may think that control also provides a begininvoke method. Let's try it:
NewThread() => {Thread. Sleep (1000 );StringbuilderSB =NewStringbuilder();For(IntI = 0; I <10000; I ++) sb. append ("Test");StringS = sb. tostring (); begininvoke (NewAction() =>{ Textbox1.text = s ;}). Start ();
It seems that there is no difference in the effect, so what is the difference between invoke and begininvoke?
We know that Windows applications are based on messages. Windows APIs provide sendmessage and postmessage APIs. The former executes messages and returns messages (without passing through the message pipeline and before postmessage ), the latter sends messages to the MPs queue for asynchronous execution. Invoke and begininvoke are similar in behavior to these two APIs, but both actually use postmessage. The former uses semaphores to block messages before they are executed to achieve synchronization. Let's do an experiment:
NewThread() => {Thread. Sleep (1000 );StringbuilderSB =NewStringbuilder();For(IntI = 0; I <10000; I ++) sb. append ("Test");StringS = sb. tostring ();StopwatchSw =Stopwatch. Startnew (); invoke (NewAction() =>{ Textbox1.text = s ;}));MessageBox. Show (SW. elapsedmilliseconds. tostring () ;}). Start ();
Run the program:
It can be seen that the pop-up box appears only after the value of the text box appears. It takes 2 seconds to assign a value to the message in the text box. Change invoke to begininvoke and execute the program without moving:
Obviously, the pop-up box displays the text box value after 2 seconds. Begininvoke does not block subsequent statement execution. Therefore, it should be noted that if the variables we use in the method are modified after begininvoke, it is very likely that confusion will occur. If you have used the delegate begininvoke, you should know that it is generally recommended that you always call endinvoke to reclaim resources. For the control endinvoke, if you do not need to obtain the return value, then it is not necessary (from msdn ).
Now you may still have questions about why begininvoke is used, and the UI is stuck for about 2 seconds. To understand this, we can assign so many texts to the text box, this UI behavior is very time-consuming. Whether invoke or begininvoke is used to send a message to the UI thread for processing (neither of them uses the thread pool), it takes so much time, in general, we will not render so much data on the UI.
In general, the optimization we can do is:
1) Try to use new threads for non-UI operations to perform asynchronous computing without blocking the UI thread. Submit the operation to the UI thread only when the UI is actually required.
2) reduce UI operation complexity as much as possible. For example, if you need to draw a complex image on the UI, you can create a bitmap first in the memory, and then draw the entire bitmap on the UI, instead of drawing the image directly on the UI
For example, the UI is like a canvas. How can we draw a masterpiece on it to take up more time and make it available to everyone? One method is that we don't occupy the cloth when preparing the color and paint brush. We only use it when we really want to paint it. The other method is to draw it first on another canvas, then print the pattern to our main canvas by copying it.
For the presentation of a large amount of data, we can also:
1) Use pagination to display only part of the data. For Windows programs, pagination may be of the scroll bar nature. When the scroll bar is pulled down, the data of the current "page" is displayed.
2) even a page of data can be partially presented.
For example, when loading a Word document, we can see the first page as soon as we open it. Then, the rolling blocks gradually become smaller and the number of pages gradually increases, if we load 1000 pages at the beginning, it may take one minute to see the first page. If we can't wait to flip the scroll bar, we will immediately load the following data:
NewThread() => {StringbuilderSB =NewStringbuilder();For(IntI = 0; I <100; I ++) sb. append ("Test");StringS = sb. tostring ();For(IntI = 0; I <20; I ++) {begininvoke (NewAction() =>{ Textbox1.text + = S + I ;}));Thread. Sleep (10) ;}}). Start ();
Set the text box to allow vertical scroll bars and run this program to realize this effect: