Document directory
- 1. Windows program message mechanism
- 2. Message loop in DOTNET
- 3. Non-thread operations on GUI controls
- Ii. Message Mechanism-Inter-thread and inter-process communication mechanism
- 1. Window message sending
- 2. invoke and begininvoke
- 3. usage issues
- 3. Delegate. begininvoke
- 4. View related code with reflector
- 1. Control. begininvoke and control. Invoke
- 3. invokerequired
I. Why does the control class provide the invoke and begininvoke mechanisms?
The main reason for this problem is already well known by dotnetprogrammers. I have recorded my own logs again here so that I can remind myself later.
1. Windows program message mechanism
The Windows GUI program is based on the message mechanism, and a main thread maintains a message pump. This message pump keeps Windows programs running.
Message loop in Windows GUI Program
A Windows program has a message queue. All messages in the form are the primary sources of messages in this queue. Here, the while loop uses the getmessage () method. This is a blocking method, that is, the method will be blocked when the queue is empty, so that the while loop stops motion, this avoids a program from exhausting the CPU for no reason, making it difficult for other programs to respond. Of course, you can use another method in some programs that require maximum CPU movement. For example, in some 3D games or strategy games, peekmessage () is generally used, it will not be blocked by windows, so as to ensure the smoothness and High Frame Rate of the entire game.
The main thread maintains the entire form and the child controls above. When it gets a message, it will call the dispatchmessage method to dispatch the message, which will cause the call to the Window Process on the form. In the window process, the form data update code and other code provided by the programmer are provided.
2. Message loop in DOTNET
Public static void main (string [] ARGs)
{
Form F = new form ();
Application. Run (f );
}
The DOTNET form program encapsulates the preceding while loop, which is started through the application. Run method.
3. Non-thread operations on GUI controls
If you operate controls on Windows forms from another thread, it will compete with the main thread, causing unpredictable results and even deadlocks. Therefore, Windows GUI programming has a rule that allows you to operate control data only by creating the control thread. Otherwise, unexpected results may occur.
Therefore, in DOTNET, to solve these problems easily, the control class implements the isynchronizeinvoke interface and provides the invoke and begininvoke methods to allow other threads to update GUI controls.
Public interface isynchronizeinvoke
{
[Hostprotection (securityaction. linkdemand, synchronization = true, externalthreading = true)]
Iasyncresult begininvoke (delegate method, object [] ARGs );
Object endinvoke (iasyncresult result );
Object invoke (delegate method, object [] ARGs );
Bool invokerequired {Get ;}
}
}
If you operate a Windows form control outside the thread, you need to use the invoke or begininvoke method and use a delegate to mail the call to the thread to which the control belongs for execution.
Ii. Message Mechanism-Inter-thread and inter-process communication mechanism 1. Window message sending
Windows message mechanism is one of the threads or inter-process communication mechanisms on Windows. Windows message value is actually a defined data structure. The most important thing is the message type, which is an integer and then the message parameter. Message parameters can represent many things.
Windows provides APIs for sending messages to a thread message queue. Therefore, a thread can send messages to the Message Queue of another thread to tell the other thread what to do, thus completing inter-thread communication. Some APIs require a window handle to send messages. This function can send messages to the main thread message queue of the specified window, while some can directly use the thread handle, send messages to the thread message queue.
Communication by message mechanism
Sendmessage is a Windows API used to send a message to a window message queue. This method is a blocking method, that is, the operating system will ensure that the message is indeed sent to the destination message queue, and the function will return after the message is processed. The caller will be temporarily blocked before return.
Postmessage is also an API function used to send messages to the window message queue, but this method is not 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
Invoke or begininvoke
The invoke or begininvoke method requires a delegate object as the parameter. The delegate is similar to the callback function address. Therefore, the caller can use these two methods to block the function address to the interface thread. If these methods contain code for changing the control status, the interface thread is used to avoid competition conditions and unexpected problems. If other threads directly operate on the control to which the interface thread belongs, competition conditions will be generated, resulting in unpredictable results.
Using invoke to complete the sending of a delegate method is similar to using the sendmessage method to send messages to the interface thread. It is a synchronous method. That is to say, the invoke method will not return until the invoke method is executed, and the caller thread will be blocked.
Use the begininvoke method to mail a delegate method, similar to using postmessage for communication, which is an Asynchronous Method. That is, the method will be returned immediately after the method is sent. It will not wait until the execution of the delegate method ends, and the caller thread will not be blocked. However, the caller can also use the endinvoke method or other similar waithandle mechanism to wait for the completion of asynchronous operations.
In terms of internal implementation, both invoke and begininvoke use the postmessage method to avoid problems caused by sendmessage. The synchronous blocking of the invoke method relies on the waithandle mechanism.
3. usage issues
If your background thread does not need to wait after updating the status of a UI control, but needs to continue processing, you should use begininvoke for asynchronous processing.
If your background thread needs to operate the UI control and wait until the operation is completed to continue, you should use invoke. Otherwise, if the background thread and the main cross-section thread share certain State data, if they do not call the data synchronously but continue to execute the data, the execution sequence may be faulty, although no deadlock occurs, unexpected display results or data processing errors may occur.
We can see that isynchronizeinvoke has an attribute called invokerequired. This attribute is used to determine whether invoke or begininvoke is required for an object to access the UI control during programming. If you do not need it, you can directly update it. This attribute returns false when the caller object and UI object belong to the same thread. In the code analysis, we can see that the implementation of the control class for this attribute is to determine whether the caller and the control belong to the same thread.
3. Delegate. begininvoke
Asynchronous calling of synchronous methods through a delegate is also one of the asynchronous calling mechanisms provided by. net. However, the delegate. begininvoke method extracts a thread from the threadpool to execute this method to obtain the asynchronous execution effect. That is to say, if multiple asynchronous delegates are submitted in this way, the order of these calls cannot be guaranteed. In addition, because the threads in the thread pool are used to complete tasks, frequent use may affect the system performance.
Delegate. begininvoke also refers to sending a delegate method to other threads, and thus executing a method through asynchronous mechanism. The caller thread can continue its work after the mail is completed. However, the final execution thread sent by this method is a thread selected by the Runtime library from the threadpool.
A misunderstanding needs to be corrected here, that is, the asynchronous call of the control class begininvoke does not open up a new thread to complete the delegate task, but rather enables the thread of the interface control to complete the delegate task. It seems that asynchronous operations are not necessarily accurate to open up new threads.
4. View related code with reflector 1. Control. begininvoke and control. Invoke
public IAsyncResult BeginInvoke(Delegate method, params object[] args)
{
using (new MultithreadSafeCallScope())
{
return (IAsyncResult) this.FindMarshalingControl().MarshaledInvoke(this, method, args, false);
}
}
public object Invoke(Delegate method, params object[] args)
{
using (new MultithreadSafeCallScope())
{
return this.FindMarshalingControl().MarshaledInvoke(this, method, args, true);
}
}
The findexternalingcontrol method traces back the parent control from the current control until it finds the top-level parent control and uses it as the encapsulated object. For example, we call the invoke method of a progress bar on the form to block the delegate, but it will actually go back to the main form and use this control object to block the delegate. Because the main form is related to the main thread message queue, messages sent to the main form can be sent to the Interface main thread message queue.
We can see that the invoke and begininvoke methods use the same implementation, but the last parameter value of the marshaledinvoke method is different.
2. marshaledinvoke
private object MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous)
{
int num;
if (!this.IsHandleCreated)
{
throw new InvalidOperationException(SR.GetString("ErrorNoMarshalingThread"));
}
if (((ActiveXImpl) this.Properties.GetObject(PropActiveXImpl)) != null)
{
IntSecurity.UnmanagedCode.Demand();
}
bool flag = false;
if ((SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, this.Handle), out num) == SafeNativeMethods.GetCurrentThreadId()) && synchronous)
{
flag = true;
}
ExecutionContext executionContext = null;
if (!flag)
{
executionContext = ExecutionContext.Capture();
}
ThreadMethodEntry entry = new ThreadMethodEntry(caller, method, args, synchronous, executionContext);
lock (this)
{
if (this.threadCallbackList == null)
{
this.threadCallbackList = new Queue();
}
}
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(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);
}
If (! Synchronous) // If asynchronous, return immediately
{
return entry;
}
If (! Entry. iscompleted) // the synchronous call is not over. Block it and wait.
{
this.WaitForWaitHandle(entry.AsyncWaitHandle);
}
if (entry.exception != null)
{
throw entry.exception;
}
return entry.retVal;
}
How do we finally see postmessage? Messages are sent through the windows message mechanism. The delegate method to be sent is passed as the message parameter. Other code is not explained here.
3. invokerequired
public bool InvokeRequired
{
get
{
using (new MultithreadSafeCallScope())
{
HandleRef ref2;
int num;
if (this.IsHandleCreated)
{
ref2 = new HandleRef(this, this.Handle);
}
else
{
Control wrapper = this.FindMarshalingControl();
if (!wrapper.IsHandleCreated)
{
return false;
}
ref2 = new HandleRef(wrapper, wrapper.Handle);
}
int windowThreadProcessId = SafeNativeMethods.GetWindowThreadProcessId(ref2, out num);
int currentThreadId = SafeNativeMethods.GetCurrentThreadId();
return (windowThreadProcessId != currentThreadId);
}
}
}
Finally, we can see that this is to judge whether the Windows form thread and the current caller thread are the same. If it is the same, there is no need to mail it out. Visit this GUI control directly. Otherwise, you don't need to say so directly. You need to use invoke or begininvoke as the media.