Preface:
When multiple threads access a thread-safe resource, such as the UI, there must be some kind of mechanism to ensure that these threads operate aling. If resources requiring thread-safe are simultaneously accessed by other threads without any measures, the following similar invalidoperationexception will be thrown.
Cross - Thread operation not valid: Control ' M_counterlabel ' Accessed from a thread other than the thread it was created on.
Excpetion screenshot is as follows:
Programming Model 1:
In the following sample, myform provides a propertity called Allow client access to obtain the mysynchronizationcontext of the UI. Mysynchronizationcontext is initialized by obtaining the context of the current thread in the constructor of myform. Myform also provides counter property to update the Windows Forms label of the server. Of course. counter can only be accessed by the current thread of the form.
Source code:
Service UI
Using System;
Using System. Collections. Generic;
Using System. componentmodel;
Using System. Data;
Using System. drawing;
Using System. LINQ;
Using System. text;
Using System. Windows. forms;
namespace uisynchronizationcontext_service
{< br> Public partial class myform: form
{< br> private system. windows. forms. label m_counterlabel;
system. threading. synchronizationcontext m_synchronizationcontext;
Public myform ()
{< br> initializecomponent ();
m_synchronizationcontext = system. threading. synchronizationcontext. current;
system. diagnostics. debug. assert (m_synchronizationcontext ! = null );
}
PublicSystem. Threading. synchronizationcontext mysynchronizationcontext
{
Get
{
ReturnM_synchronizationcontext;
}
}
Public Int Counter
{
Get { Return Convert. toint32 (m_counterlabel.text );}
Set {M_counterlabel.text = Value. tostring ();}
}
Private Void Myform_load ( Object Sender, eventargs E)
{
}
}
}
Service Contract and implementation
Using System;
Using System. Collections. Generic;
Using System. LINQ;
Using System. text;
Using System. servicemodel;
NamespaceUisynchronizationcontext_service
{
[Servicecontract]
Public InterfaceIformmanager
{
[Operationcontract (isoneway= True)]
VoidIncrementlabel ();
}
Class Mycontract: iformmanager
{
Public Void Incrementlabel ()
{
Myform form = System. Windows. Forms. application. openforms [ 0 ] As Myform;
System. Diagnostics. Debug. Assert (Form ! = Null );
Sendorpostcallback callback = Delegate
{
Form. Counter ++ ;
};
Form. mysynchronizationcontext. Send (callback,Null);
}
}
Static Class Program
{
/// <Summary>
/// The main entry point for the application.
/// </Summary>
[Stathread]
Static Void Main ()
{
Application. enablevisualstyles ();
Application. setcompatibletextrenderingdefault ( False );
Servicehost host= NewServicehost (Typeof(Mycontract ));
Host. open ();
Application. Run (NewMyform ());
Host. Close ();
}
}
}
Running result:
Service updated UI by multiple threads
#1 client CILS for service to update service's UI
#2 client CILS for service to update service's UI
Disadvantages:
The defiency of the programming model is that the service and UI form are too coupled. Obviously, this is a bad design model. It is inconvenient to update multiple controls in form. The ideal design method is to use the service and UI form decouple, which are independent of each other.
Programming Model 2:
So how to implement this programming model? We can customize thread-safe control. Encapsulate the thread-safe operations required for synchronization context with Windows form in these so-called safe controls to decouple them from the service.
For exampleCodeSlight modification:
1. we add a m_mytextbox reference to form, but in fact this m_mytextbox is a reference of mytexbbox, which is a custom derived from system. windows. forms. safe textbox class of text. We override some of its methods or propertity so that it runs under thread-safe:
The following is the definition of the custom textbox mytextbox.
Using System;
Using System. Collections. Generic;
Using System. LINQ;
Using System. text;
Using System. Windows. forms;
Using System. Threading;
NamespaceUisynchronizationcontext_service
{
ClassSafetextbox: textbox
{
Synchronizationcontext m_synchronizationcontext=Synchronizationcontext. Current;
Public Override String Text
{
Set
{
Sendorpostcallback settext = Delegate ( Object Text)
{
Base . Text = Text As String ;
};
M_synchronizationcontext.send (settext, value );
}
Get
{
String Text = String. empty;
Sendorpostcallback gettext = Delegate
{
Text = Base . Text;
};
M_synchronizationcontext.send (gettext, Null );
Return Text;
}
}
}
}
Assume that an operation is added to servicecontract:
[Operationcontract (isoneway = true)]
Void updatetextbox (string textboxvalue );
Used to Modify text in textbox.
Its implementation Implementation is as follows:
Class Mycontract: iformmanager
{
Public Void Incrementlabel ()
{
Myform form = System. Windows. Forms. application. openforms [ 0 ] As Myform;
System. Diagnostics. Debug. Assert (Form ! = Null );
Sendorpostcallback callback = Delegate
{
Form. Counter ++ ;
};
Form. mysynchronizationcontext. Send (callback,Null);
}
Public Void Updatetextbox ( String Textboxvalue)
{
Myform form = System. Windows. Forms. application. openforms [ 0 ] As Myform;
Form. textboxvalue=Textboxvalue;
}
}
There is only one very simple statement! All the synchronization context operations on the control are encapsulated in its safe control: So cool.
Of course, the client code is also modified as follows:
Using System;
Using System. Collections. Generic;
Using System. LINQ;
Using System. text;
Namespace Uisynchronizationcontext_client
{
Class Program
{
Static Void Main ( String [] ARGs)
{
Formmanagerclient proxy = New Formmanagerclient ();
Console. writeline ( " Press any key to continue processing " );
Console. Readline ();
Proxy. incrementlabel ();
For ( Int I = 0 ; I < 10 ; I ++ )
{
Proxy. updatetextbox ( " Value set from #1 client is " + I. tostring ());
System. Threading. thread. Sleep ( 2000 );
}
Console. writeline ( " Updated service's UI " );
Console. Readline ();
}
}
}
In order to see how the safe control of the service authenticates these update control requests, this operation is specially given for 10 consecutive times.
How can multiple different clients call the service continuously? We can see that the text in the textbox of the service is continuously updated by different client threads.
Running result:
The service UI running result is as follows:
for source code regarding to this article, pls press here to download