157 recommendations for writing high-quality code to improve C # programs--Recommendation 87: Differentiate between WPF and WinForm threading models

Source: Internet
Author: User
Tags message queue

Recommendation 87: Differentiate between WPF and WinForm threading models

Both WPF and WinForm form applications have a requirement that UI elements (such as button, TextBox, and so on) must be updated by the thread that created it. WinForm is not very restrictive in this regard, so code like the following can be run in most cases in WinForm (this recommendation will explain in detail why this happens):

Private voidButtonstartasync_click (Objectsender, EventArgs e) {Task T=NewTask (() =        {               while(true) {Label1. Text=DateTime.Now.ToString (); Thread.Sleep ( +);      }          }); //If there is an exception, start a new taskT.continuewith (Task) =    {          Try{task.}          Wait (); }          Catch(AggregateException ex) {foreach(Exception innerinchEx. InnerExceptions) {MessageBox.Show (string. Format ("Exception type: {0}{1} from: {2}{3} exception content: {4}", Inner. GetType (), Environment.NewLine,Inner. Source, Environment.NewLine, inner.              Message));      }}}, taskcontinuationoptions.onlyonfaulted);  T.start (); } 

However, the same piece of code, if placed in a WPF environment, is bound to throw a System.InvalidOperationException exception.


Theoretically, the threading model for WinForm and WPF is very close, and they end up calling APIs (GetMessage or PeekMessage) to handle messages sent by other lines Cheng, which are stored in a message queue in the system. In WinForm and WPF, the thread that creates the main interface is the main threaded thread, which is the UI thread that handles the message queue. It's just that the two are slightly different on the upper-level mechanism of handling Message Queuing, which results in the same code getting different outcomes.

has an ISynchronizeInvoke interface in the WinForm framework, and all UI elements (represented by control) inherit the interface. Where the Invokdrequired property in the interface indicates whether the current thread is the thread that created it. The Invoke and BeginInvoke methods in the interface are responsible for sending the message to the message queue so that the UI thread can handle it correctly. Well, the above code has an improved version on WinForm (only the While loop part):

 while  (true    ( Label1. invokerequired) Label1.                  BeginInvoke ( new  Action (() => = DateTime.Now.ToString ();      }));  else   Label1.      Text  = DateTime.Now.ToString ();  Thread.Sleep ( 1000  ); } 

The BeginInvoke method accepts a parameter of type delegate, where we implement it with an action.

The threading model of a WPF application relies entirely on the dispatcherobject type. All WPF controls inherit from an abstract class of visual, and this abstract class eventually inherits from the Dispatcherobject type. There is one attribute in this dispatcherobject type, two methods. The Dispatcher property completes the scheduling task between all worker threads and the UI thread. The CheckAccess method is responsible for detecting whether the worker thread has access to the control and, if so, returns True, otherwise false. The VerifyAccess method is responsible for detecting whether the worker thread has access to the control and throws an exception if it cannot be accessed InvalidOperationException.

WinForm applications use similar checkaccess to Judge Access, and WPF applications are improved, and all UI controls are judged in a verifyaccess way for worker thread access. This directly determines the output of the example at the beginning of this recommendation, and WPF simply throws an exception if it determines that the worker and UI threads are not the same thread, while WinForm has the leeway to execute successfully. However, this mechanism of WinForm directly causes the instability of the program, because even in most cases the code works well, but in the uncertain case, the worker threads in that code manipulate the UI element directly, which throws an exception.

Taking into account the limitations of WinForm on this issue, the WinForm threading model processing is again improved:

//used to represent the main thread, in this case the UI threadThread Mainthread; BOOLCheckAccess () {returnMainthread = =Thread.CurrentThread; }   voidverifyaccess () {if(!CheckAccess ())Throw NewInvalidOperationException ("The calling thread cannot access this object because another thread owns the object"); }   Private voidButtonstartasync_click (Objectsender, EventArgs e) {      //The current thread is the main pathMainthread =Thread.CurrentThread; Task T=NewTask (() =        {               while(true)              {                  if(!CheckAccess ()) Label1. BeginInvoke (NewAction (() ={Label1. Text=DateTime.Now.ToString ();                  })); ElseLabel1. Text=DateTime.Now.ToString (); Thread.Sleep ( +);      }          }); //If there is an exception, start a new taskT.continuewith (Task) =    {          Try{task.}          Wait (); }          Catch(AggregateException ex) {foreach(Exception innerinchEx. InnerExceptions) {MessageBox.Show (string. Format ("Exception type: {0}{1} from: {2}{3} exception content: {4}", Inner. GetType (), Environment.NewLine, inner. Source, Environment.NewLine, inner.              Message));      }}}, taskcontinuationoptions.onlyonfaulted);  T.start (); } 

In this code, we simulate the two methods of Dispatcherobject in WPF, CheckAccess and VerifyAccess, to re-process the threading model and enhance the stability of the system. In practical work, we can also extract the two methods as extension methods so that all UI types in the project can be used.

WPF supports both methods, with all the code shown below (note the While loop section):

Private voidButtonStart_Click (Objectsender, RoutedEventArgs e) {Task T=NewTask (() =    {           while(true)          {               This. Dispatcher.begininvoke (NewAction (() ={Textblock1.text=DateTime.Now.ToString ();              })); Thread.Sleep ( +);      }      }); //to catch an exception, a new task is startedT.continuewith (Task) =    {          Try{task.}          Wait (); }          Catch(AggregateException ex) {foreach(Exception innerinchEx. InnerExceptions) {MessageBox.Show (string. Format ("Exception type: {0}{1} from: {2}{3} exception content: {4}", Inner. GetType (), Environment.NewLine, inner. Source, Environment.NewLine, inner.              Message));      }}}, taskcontinuationoptions.onlyonfaulted);  T.start (); } 


Note For illustrative purposes, the exception in this recommendation is not passed to the main thread. In the actual coding, you should always consider wrapping the exception to the main thread.

Turn from: 157 recommendations for writing high-quality code to improve C # programs Minjia

157 recommendations for writing high-quality code to improve C # programs--Recommendation 87: Differentiate between WPF and WinForm threading models

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.