2009年2月12日 星期四

Delegate and Thread-Safe

中文版按這裡

In two previous articles, "POP3 == RFC 1939" and "What the hell is "=?big5?B? "?" (They were written in Chinese and I'll translate those into English afterward.), which discuss about reading messages from a POP3 server and decoding Chinese characters from header of messages. It is a problem to interrupting the message reading for the program mentioned above, due to single thread execution. It means only one thread is used for executing the program; And it is not able to monitor requests from user, such as a "Cancel" button has been clicked by user, while this single thread is busy for getting messages from POP3 server. To quit the program seems the only way to interrupt message reading during a long waiting time cause by a large amount of messages on server.  However, we can let the program check requests from user after pausing message reading, and then does the exact same things in next loops till the end/stop of reading. It'll cost a longer reading period of time and make the program more complicated. Multithreading is another alternative solution and it is an easier one for programming, frankly. For example, use the original tread to monitor requests form user and an additional one for messages reading. Multithreading might help us out of this; nevertheless, it causes new problems! That is, threads compete against each others for resources; it makes executions unpredictable! We don't want to see these happening. Let's read related information on MSDN: "Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way." It has to be tested which thread the control belongs to before manipulating it, in order to keep away from those situations of race conditions and deadlocks. A thread is allowed to manipulate a control, if it was created by this thread. Otherwise, this thread has to "ask" help from the one that created that control; This is so-called "Thread-Safe"You might want to know what on earth the "Thread-Safe" is, please refers to the article "Thread-Safe的理解與分析" of "蕭沖的書房", you might be able to get a rough idea about it! There is copious example on MSDN for explanation about thread-safe, following are excerpt from the example. It's main concept is that checking the value of property InvokeRequired to decide whether the caller must call an Invoke method when making method calls to the control because the caller is on a different thread than the one the control was created on. This means that InvokeRequired can return false if Invoke is not required (the call occurs on the same thread). And "Delegate" is a helpful tool in the case of Invoke is required. Check it out following: namespace CrossThreadDemo { public class Form1 : Form { // This delegate enables asynchronous calls for setting // the text property on a TextBox control. // (And it will be applied for making the method "Invoke" call.) delegate void SetTextCallback(string text); ... Following is the major part of thread-safe for setting a property of control. // This method demonstrates a pattern for making thread-safe // calls on a Windows Forms control. // // If the calling thread is different from the thread that // created the TextBox control, this method creates a // SetTextCallback and calls itself asynchronously using the // Invoke method. // // If the calling thread is the same as the thread that created // the TextBox control, the Text property is set directly. private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. // (the Delegate "SetTextCallback" can be find here.) if (this.textBox1.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } }

沒有留言: