Make Windows Forms thread-safe)

Source: Internet
Author: User
Introduction

For Windows Forms User Interface Programming, if multithreading is not used, the program is straightforward.
However, in practical applications, multithreading is required to ensure UI responsiveness. This makes interface development complicated.

Problems encountered

As you know, Windows forms is not thread-safe. For example, unless you control the message queue
It is not safe to read or write the attribute values of a control on Windows. forms. The point here is that you can only use messages
The queue thread modifies the controls on your windows forms.

Standard Solution

Of course, we have a mechanism to solve this problem. For every Windows Forms control, there is an invokerequired
. If the value of this attribute is false, the current thread is the message queue thread, and you can perform operations on the properties of the control.
Secure read/write. In addition, there is also a method called invokde, which puts a delegate and its parameters together into a control.
Waiting for calling in the message queue.
Because the call to delegate is directly initiated from the message queue, there is no threading related programming content. However
Type programming is boring. Just to set the text of the next text, or enabling/disabling a control, you will
You need to define a separate method and the corresponding delegate.

Example: random string

To illustrate this situation, I wrote a small Windows Forms Program to generate a random string. The following code snippet demonstrates how
Synchronization is performed between the message loop queue and the working thread.

  1. CharPickrandomchar (StringDigits)
  2. {
  3. Thread. Sleep (100 );
  4. ReturnDigits [random. Next (digits. Length)];
  5. }
  6. Delegate VoidSetbooldelegate (BoolParameter );
  7. VoidSetinputenabled (BoolEnabled)
  8. {
  9. If(! Invokerequired)
  10. {
  11. Button1.enabled = enabled;
  12. Comboboxdigits. Enabled = enabled;
  13. Numericupdowndigits. Enabled = enabled;
  14. }
  15. Else 
  16. Invoke (NewSetbooldelegate (setinputenabled ),New Object[] {Enabled });
  17. }
  18. Delegate VoidSetstringdelegate (StringParameter );
  19. VoidSetstatus (StringStatus ){
  20. If(! Invokerequired)
  21. Labelstatus. Text = status;
  22. Else 
  23. Invoke (NewSetstringdelegate (setstatus ),New Object[] {Status });
  24. }
  25. VoidSetresult (StringResult ){
  26. If(! Invokerequired)
  27. Textboxresult. Text = result;
  28. Else
  29. Invoke (NewSetstringdelegate (setresult ),New Object[] {Result });
  30. }
  31. Delegate IntGetintdelegate ();
  32. IntGetnumberofdigits ()
  33. {
  34. If(! Invokerequired)
  35. Return(Int) Numericupdowndigits. value;
  36. Else 
  37. Return(Int) Invoke (NewGetintdelegate (getnumberofdigits ),Null);
  38. }
  39. Delegate StringGetstringdelegate ();
  40. StringGetdigits ()
  41. {
  42. If(! Invokerequired)
  43. ReturnComboboxdigits. text;
  44. Else
  45. Return(String) Invoke (NewGetstringdelegate (getdigits ),Null);
  46. }
  47. VoidWork ()
  48. {
  49. Try 
  50. {
  51. Setinputenabled (False);
  52. Setstatus ("working ");
  53. IntN = getnumberofdigits ();
  54. StringDigits = getdigits ();
  55. Stringbuilder text =NewStringbuilder ();
  56. For(IntI = 0; I! = N; I ++)
  57. {
  58. Text. append (pickrandomchar (digits ));
  59. Setresult (text. tostring ());
  60. }
  61. Setstatus ("ready ");
  62. }
  63. Catch(Threadabortexception)
  64. {
  65. Setresult ("");
  66. Setstatus ("error ");
  67. }
  68. Finally 
  69. {
  70. Setinputenabled (True);
  71. }
  72. }
  73. VoidStart ()
  74. {
  75. Stop ();
  76. Thread =NewThread (NewThreadstart (work ));
  77. Thread. Start ();
  78. }
  79. VoidStop ()
  80. {
  81. If(Thread! =Null)
  82. {
  83. Thread. Abort ();
  84. Thread =Null;
  85. }
  86. }

I used thread. Abort because it is just a simple example. If you are performing an operation that cannot be interrupted under any circumstances,
Use a flag to notify your thread.
The above code contains a lot of simple and repetitive code. For example, before calling invoke, you must always check invokerequired. Because
If you call invoke before creating a message queue, an error occurs.

Generated thread security wrappers

In the previous article, I introduced how to automatically generate a wrappers class to "implicitly" implement an interface. Extension of the same Code,
You can create wrppers and then automatically let the thread call the correct method.

I will explain how the entire mechanism works later. First, let's take a look at how it works.

First, you can publish relevant attributes without worrying about thread programming. This is what you plan to do even if you do not use multithreading.

  1. Public BoolInputenabled
  2. {
  3. Set
  4. {
  5. Button1.enabled = value;
  6. Comboboxdigits. Enabled = value;
  7. Numericupdowndigits. Enabled = value;
  8. }
  9. }
  10. Public StringStatus
  11. {
  12. Set{Labelstatus. Text = value ;}
  13. }
  14. Public IntNumberofdigits
  15. {
  16. Get{ReturnNumericupdowndigits. value ;}
  17. }
  18. Public StringDigits
  19. {
  20. Get{ReturnComboboxdigits. Text ;}
  21. }
  22. Public StringResult
  23. {
  24. Set{Textboxresult. Text = value ;}
  25. }

Then, you define an interface that contains all the attributes or methods you intend to call from another thread.

  1. InterfaceIformstate
  2. {
  3. IntNumberofdigits {Get;}
  4. StringDigits {Get;}
  5. StringStatus {Set;}
  6. StringResult {Set;}
  7. BoolInputenabled {Set;}
  8. }

Now, in the working thread, all you have to do is create a thread-safe wrapper and use it. You no longer need to input repeated code.

  1. VoidWork ()
  2. {
  3. Iformstate state = wrapper. Create (Typeof(Iformstate ),This);
  4. Try 
  5. {
  6. State. inputenabled =False;
  7. State. Status = "working ";
  8. IntN = state. numberofdigits;
  9. StringDigits = state. digits;
  10. Stringbuilder text =NewStringbuilder ();
  11. For(IntI = 0; I <n; I ++)
  12. {
  13. Text. append (pickrandomchar (digits ));
  14. State. Result = text. tostring ();
  15. }
  16. State. Status = "ready ";
  17. }
  18. Catch(Threadabortexception)
  19. {
  20. State. Status = "error ";
  21. State. Result = "";
  22. }
  23. Finally
  24. {
  25. State. inputenabled =True;
  26. }
  27. }

Working mechanism

The wrapper generator uses system. reflection. emit to generate a proxy class. This proxy class contains all the methods required by the interface, and it also contains access attributes.
Each method has a specific signature ).

In the method body, if the return value of invokerequired is false, the original method is called directly. This check is very important, in order
When attached is sent to a message thread, the call can also work.

If invokerequired returns true, a delegate pointing to the original method is passed as a parameter that calls the form's invode method. Delegate
In this way, the delegate type is not created repeatedly for methods with the same signature.

Since the wrapper generator uses the isynchronizeinvoke interface for Synchronous calling, you can use it in non-Windows-forms programs. What you want to do
It is just the implementation interface and probably its own implementation of something similar to message queue.

Limitations and warnings

One important thing to understand is that thread-safe wrapper is used to hide thread synchronization, but it does not mean that thread synchronization is not performed.
Therefore, if a thread-safe wrapper is used to access an attribute, it is much slower to directly access this attribute when invokerequired returns true. Therefore,
If you make complex changes to your form from several different threads, it is best to put them in a method to complete it once instead of calling it several times separately.

Another thing to keep in mind is that not all types are safe to pass between threads without synchronization. Generally, only int, datetime, and
Some immutable reference types, such as string, are safe. If you want to pass a mutable reference type from one thread to another, for example
Stringbuilder, so you must be careful. If this object is not modified in different threads, or it is thread-safe, the transfer is OK. If
If you have any questions, make a deep copy transfer. Do not use references.

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.