1. Windows program Message mechanism
The Windows GUI program is message-based and has a main thread that maintains the message pump. This message pump keeps Windows programs alive.
Windows programs have a message queue, and all messages on the form are the most important source of messages inside this queue. Here the while loop uses the GetMessage () method, which is a blocking method, that is, when the queue is empty, the method blocks, and the while loop stops moving, which avoids a program draining the CPU for no apparent reason, making it difficult for other programs to get a response. Of course, in some programs that require maximum CPU movement, you can use another method, such as some 3d games or timely strategy games, usually use PeekMessage () This method, it will not be blocked by Windows, so as to ensure the smooth and relatively high frame rate of the whole game. (PeekMessage is a Windows API function.) The function checks the thread message queue for a message and places the message (if it exists) in the specified structure. The DispatchMessage function is to send a message to the window, the window receives the message, and executes the event.
This main thread maintains the entire form and the child controls above it. When it gets a message, it calls the DispatchMessage () method to dispatch the message, which causes a call to the window procedure on the form. The window procedure is of course the programmer provides the form data update code and other code.
2. dotnet inside the message loop
Public Static void Main (string[] args) { new Form (); Application.Run (f);}
The Dotnet form program encapsulates the while loop, which is initiated by the Application.Run method.
3. Problems with the GUI control outside the thread operation
If you manipulate a control on a Windows Form from another thread, it competes with the main thread, causing unpredictable results or even deadlocks. So there is a rule in Windows GUI programming that the control's data can be manipulated only by creating the control's thread, otherwise unpredictable results can occur.
Therefore, in order to solve these problems conveniently, the control class implements the ISynchronizeInvoke interface and provides the Invoke and BeginInvoke methods to provide a mechanism for dotnet other threads to update GUI interface controls.
Public Interface isynchronizeinvoke{ [HostProtection (SecurityAction.LinkDemand, synchronization=true, Externalthreading=true)] object[] args); Object EndInvoke (IAsyncResult result); Object Object [] args); BOOL Get ; }}}
If you are manipulating Windows Forms controls from outside the thread, you need to marshal the calls to the thread on which the control belongs by using the Invoke or BeginInvoke method.
II. Message Mechanism---Inter-threading and inter-process Communication mechanism 1, window message sending
The Windows Messaging mechanism is a thread on the Windows platform or one of the interprocess communication mechanisms. The Windows message value is actually a defined data structure, the most important is the type of the message, it is an integer, and then the parameters of the message. The parameters of a message can represent many things.
Windows provides some APIs for sending messages to a thread's message queue. Therefore, one thread can send a message to another thread's message queue to tell the other what to do, thus completing the communication between the threads. Some APIs send messages that require a window handle that can send messages to the main thread message queue of a specified window, while others can send messages to the threads message queue directly through the thread handle.
SendMessage is the Windows API, which is used to send a message to a window's message queue. This method is a blocking method, which means that the operating system will ensure that the message is actually sent to the destination message queue, and that the function is returned after the message has been processed. Before returning, the caller will be temporarily blocked.
PostMessage is also an API function used to send messages to a window message queue, but this method is non-blocking. That is, it will return immediately, regardless of whether the message is actually sent to the destination, that is, the caller will not be blocked.
2. Invoke and BeginInvoke
Either the Invoke or BeginInvoke method requires a delegate object as a parameter. The delegate is similar to the address of the callback function, so the caller can then marshal the function address that needs to be called to the interface thread. In these methods, if you include code that changes the state of the control, it is the interface thread that eventually executes the method, which avoids the competitive conditions and avoids unforeseen problems. If other threads directly manipulate the control that the interface thread belongs to, then there will be a race condition, causing unpredictable results.
Using invoke to complete marshaling of a delegate method is similar to using the SendMessage method to send a message to the interface thread, which is a synchronous method. This means that the Invoke method does not return until the method of the invoke Marshal is executed, and the caller thread is blocked.
Marshaling a delegate method using the BeginInvoke method, similar to communicating using PostMessage, is an asynchronous method. That is, the method returns immediately after marshaling, does not wait for the execution of the delegate method to end, and the caller thread will not be blocked. But callers can also use the EndInvoke method or other similar WaitHandle mechanisms to wait for the completion of an asynchronous operation.
But in the internal implementation, both invoke and BeginInvoke use the PostMessage method, which avoids the problem of SendMessage. The synchronous blocking of the Invoke method is done by the WaitHandle mechanism.
3, the use of the occasion
If your background thread does not need to wait after updating the state of a UI control, but to continue working on it, then you should use BeginInvoke for asynchronous processing.
You should use invoke if your background thread needs to manipulate the UI control and needs to wait until the operation is complete before it can continue. Otherwise, in the case where the background thread and the main section thread share some state data, if the call is not synchronized and the execution continues, it may cause problems on the sequence of execution, although no deadlock occurs, but unexpected display results or data processing errors occur.
You can see that ISynchronizeInvoke has a property, invokerequired. This property is used when programming to determine whether an object needs to be marshaled using Invoke or BeginInvoke when accessing the UI control. You can update it directly if you don't need it. This property returns False when the caller object and the UI object belong to one thread. In the subsequent code analysis, we can see that the control class's implementation of this property is to determine whether the caller and the control belong to the same thread.
Third, Delegate.begininvoke
asynchronous invocation of a synchronous method through a delegate is also one of the asynchronous invocation mechanisms provided by. NET. But the Delegate.begininvoke method is to take a thread out of ThreadPool to execute this method to get an asynchronous execution effect. That is, if you commit multiple asynchronous delegates in this way, the order of these calls cannot be guaranteed. And because of the use of threads inside the thread pool to accomplish tasks, frequent use can have an impact on the performance of the system.
Delegate.begininvoke also tells a delegate method to marshal to another thread, thereby executing a method through an asynchronous mechanism. The caller thread can continue its work after the marshaling is complete. But the final thread of execution that this method marshals to is the one that the runtime picks from the ThreadPool.
One of the pitfalls here is that the asynchronous invocation of BeginInvoke on the control class does not open a new thread to complete the delegate task, but instead lets the interface control's owning thread complete the delegated task. It seems that asynchronous operations are not necessarily accurate in opening new threads.
Iv. use reflector to see some relevant code 1, Control.BeginInvoke and Control.Invoke
PublicIAsyncResult BeginInvoke (Delegate method,params Object[] args) { using(NewMultithreadsafecallscope ()) { return(IAsyncResult) This. Findmarshalingcontrol (). Marshaledinvoke ( This, method, Args,false); }} Public ObjectInvoke (Delegate method,params Object[] args) { using(NewMultithreadsafecallscope ()) { return This. Findmarshalingcontrol (). Marshaledinvoke ( This, method, Args,true); }}
The Findmarshalingcontrol method here loops up through a loop, backtracking the parent control from the current control until it finds the top-level parent control, which is used as the marshaling object. For example, we call the Invoke method of the previous progress bar on the form to marshal the delegate, but actually backtrack to the main form and marshal the delegate through the control object. Because the main form is related to the main thread message queue, messages sent to the main form can be sent to the interface's main thread message queue.
We can see that the invoke and BeginInvoke methods use the same implementation, except that the last parameter value of the Marshaledinvoke method is different.
2.
2, Marshaledinvoke
Private ObjectMarshaledinvoke (Control caller, Delegate method,Object[] args,BOOLsynchronous) { intnum; if(! This. ishandlecreated) {Throw NewInvalidOperationException (SR. GetString ("Errornomarshalingthread")); } if((Activeximpl) This. Properties.getobject (PROPACTIVEXIMPL))! =NULL) {IntSecurity.UnmanagedCode.Demand (); } BOOLFlag =false; if((Safenativemethods.getwindowthreadprocessid (NewHandleRef ( This, This. Handle), outnum) = = Safenativemethods.getcurrentthreadid ()) &&synchronous) {Flag=true; } executioncontext ExecutionContext=NULL; if(!flag) {ExecutionContext=executioncontext.capture (); } Threadmethodentry Entry=NewThreadmethodentry (caller, method, args, synchronous, executioncontext); Lock( This) { if( This. threadcallbacklist = =NULL) { This. threadcallbacklist =NewQueue (); } } Lock( This. Threadcallbacklist) { if(Threadcallbackmessage = =0) {Threadcallbackmessage= Safenativemethods.registerwindowmessage (application.windowmessagesversion +"_threadcallbackmessage"); } This. Threadcallbacklist.enqueue (entry); } if(flag) { This. Invokemarshaledcallbacks (); } Else { //finally found you, PostMessage.Unsafenativemethods.postmessage (NewHandleRef ( This, This. Handle), Threadcallbackmessage, IntPtr.Zero, IntPtr.Zero); } if(!synchronous)//if it's asynchronous, go back right now. { returnentry; } if(!entry. iscompleted)//synchronous call is not over, block up and wait. { This. Waitforwaithandle (entry. AsyncWaitHandle); } if(Entry.exception! =NULL) { Throwentry.exception; } returnEntry.retval;}
What, did we finally see PostMessage? Marshaling is implemented through the Windows Messaging mechanism. The delegate method that needs to be marshaled is passed as a parameter of the message. There is no further explanation for the other code.
3, InvokeRequired
Public BOOLinvokerequired{Get { using(NewMultithreadsafecallscope ()) {HandleRef ref2; intnum; if( This. ishandlecreated) {Ref2=NewHandleRef ( This, This. Handle); } Else{Control wrapper= This. Findmarshalingcontrol (); if(!Wrapper. ishandlecreated) {return false; } Ref2=NewHandleRef (wrapper, wrapper. Handle); } intWindowthreadprocessid = Safenativemethods.getwindowthreadprocessid (Ref2, outnum); intCurrentthreadid =Safenativemethods.getcurrentthreadid (); return(Windowthreadprocessid! =Currentthreadid); } }}
Finally see, this is to determine whether the Windows Forms thread and the current caller thread is the same, if the same is not necessary to marshal, direct access to the GUI control bar. Otherwise, do not be so straightforward to vindicate, you need to invoke or BeginInvoke matchmaking.
Windows program message mechanism (WinForm interface Update)--Go