Cross-thread call of Form Controls

Source: Internet
Author: User

This article reprinted: http://www.csharpwin.com/csharpspace/11279r6763.shtml

 

It is wise to use multithreading when executing a time-consuming operation. It can improve the response speed of the program UI and make everything run faster. Multi-threaded programming in Windows was once the exclusive privilege of C ++ developers, but now it can be written in all languages compatible with Microsoft. NET.

However, the Windows form architecture imposes strict rules on thread usage. If you only write a single-threaded application, you do not need to know these rules because the single-threaded Code cannot violate these rules. However, once multithreading is adopted, you need to understand the most important thread rule in Windows Forms: except for a few exceptions, otherwise, do not use any member of the control in a thread other than its creation thread. The exceptions of this rule are described in the document, but there are very few such exceptions. This applies to any objects whose classes are derived from System. Windows. Forms. Control, including almost all elements in the UI. All the UI elements (including the form itself) are derived from the Control class. In addition, the result of this rule is that a contained control (such as a button contained in a form) must be in the same thread as the control bit that contains it. That is to say, all controls in a window belong to the same UI thread. In reality, most Windows Forms applications eventually have only one thread, and all UI activities occur on this thread. This thread is usually called a UI thread. This means that you cannot call any methods on any controls on the user interface, unless it is indicated in the description of this method. There are few exceptions for this rule (there is always a document record) and there is little relationship between them. Note that the following code is invalid:

Private Thread myThread;

Private void Form1_Load (object sender, EventArgs e)

{

MyThread = new Thread (new ThreadStart (RunsOnWorkerThread ));

MyThread. Start ();

}

Private void RunsOnWorkerThread ()

{

Label1.Text = "myThread thread calls UI control ";

}

If you try to run this code in. NET Framework 1.0, you may be lucky to run it, or it seems like this at first. This is the main problem in multithreading errors, that is, they are not immediately displayed. Even when some errors occur, everything looks normal before the first demo. But do not make a mistake-the code I just showed clearly violates the rules and can foresee any hope that "the trial run is good, there should be no problem. "people will pay a heavy price in the coming debugging period.

 

Next, let's take a look at the methods to solve this problem.

 
1. The System. Windows. Forms. MethodInvoker type is a System-defined delegate used to call methods without parameters.
Private Thread myThread;

Private void Form1_Load (object sender, EventArgs e)

{

MyThread = new Thread (new ThreadStart (RunsOnWorkerThread ));

MyThread. Start ();

}

Private void RunsOnWorkerThread ()

{

MethodInvoker mi = new MethodInvoker (SetControlsProp );

BeginInvoke (mi );

}

Private void SetControlsProp ()

{

Label1.Text = "myThread thread calls UI control ";

}

2. directly use System. EventHandle (with parameters)

Private Thread myThread;

Private void Form1_Load (object sender, EventArgs e)

{

MyThread = new Thread (new ThreadStart (RunsOnWorkerThread ));

MyThread. Start ();

}

Private void RunsOnWorkerThread ()

{

// DoSomethingSlow ();

String pList = "myThread thread calls UI control ";

Label1.BeginInvoke (new System. EventHandler (UpdateUI), pList );

}

// Directly use System. EventHandler, and there is no need to customize the delegate

Private void UpdateUI (object o, System. EventArgs e)

{

// Set the label1 attribute in the UI thread

Label1.Text = o. ToString () + "successful! ";

}

 
3. Packaging Control. Invoke
 

Although the Code in the second method solves this problem, it is quite tedious. If the auxiliary thread wants to provide more feedback at the end, rather than simply giving "Finished !" The BeginInvoke is too complex to use. To send other messages, such as "processing" and "everything goes smoothly", you need to try to pass a parameter to the UpdateUI function. You may also need to add a progress bar to improve the feedback capability. So many calls to BeginInvoke may cause the auxiliary thread to be dominated by the Code. This will not only cause inconvenience, but also take into account the coordination between the auxiliary thread and the UI, this design is not good. After the analysis, we think that the packaging function can solve these two problems.

Private Thread myThread;

Private void Form1_Load (object sender, EventArgs e)

{

MyThread = new Thread (new ThreadStart (RunsOnWorkerThread ));

MyThread. Start ();

}

Private void RunsOnWorkerThread ()

{

//// DoSomethingSlow ();

For (int I = 0; I <100; I ++)

{

ShowProgress (Convert. ToString (I) + "%", I );

Thread. Sleep (100 );

}

}

Public void ShowProgress (string msg, int percentDone)

{

// Wrap the parameters in some EventArgs-derived custom class:

System. EventArgs e = new MyProgressEvents (msg, percentDone );

Object [] pList = {this, e };

 

BeginInvoke (new MyProgressEventsHandler (UpdateUI), pList );

}

Private delegate void MyProgressEventsHandler (object sender, MyProgressEvents e );

Private void UpdateUI (object sender, MyProgressEvents e)

{

LblStatus. Text = e. Msg;

MyProgressControl. Value = e. PercentDone;

}

Public class MyProgressEvents: EventArgs

{

Public string Msg;

Public int PercentDone;

Public MyProgressEvents (string msg, int per)

{

Msg = msg;

PercentDone = per;

}

}

The ShowProgress method encapsulates the work that directs the call to the correct thread. This means that the Helper thread Code does not need to pay too much attention to the UI details, but only needs to call ShowProgress regularly.

If I provide a public method designed to be called from any thread, it is entirely possible that someone will call this method from the UI thread. In this case, there is no need to call BeginInvoke because I am already in the correct thread. Calling Invoke is a waste of time and resources. It is better to call appropriate methods directly. To avoid this, the Control class exposes an attribute called InvokeRequired. This is another exception to the "UI thread only" rule. It can be read from any thread. If the calling thread is a UI thread, false is returned, and other threads return true. This means that you can modify the packaging as follows:

Public void ShowProgress (string msg, int percentDone)

{

If (InvokeRequired)

{

// As before

//...

}

Else

{

// We're already on the UI thread just

// Call straight through.

UpdateUI (this, new MyProgressEvents (msg, PercentDone ));

}

}

Refer:

Http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/
MisMultithreading. mspx? Mfr = true

 

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.