Welcome to Tech-Review.Org Sign in | Join | Help

.net_2.0

My coding blog entries. Typically will either be more complex coding examples or overcoming product issues / troubleshooting resolutions.
Thread Safety...and Perf Tips c#

One of the aspects of programming web oriented applications, that I have never actually considered - is the concept of thread safety.  Color me unaware; but, just in the method  which I program - and the fact I have never encountered an 'thread' issue - doesn't mean ignorance is bliss... Whether you are writing a web application that uses the default ISAPI handler or if you roll your own custom IHTTP handler... it is possible to inflict thread safety issues into your application. So, if you are having issues with unexpected data between users, sessions or the likes - this is your article....

So, what is thread safety and why should you be concerned? 

Thread safety denotes when data expected is different from what is expected.  Particularly, it is related to marking certain classes or methods as static - which means that only one instance of that class or method will be in memory. In other words it is shared amongst numerous threads across the application.  The misconception I at least had is that static meant nothing ever changes but in the programming world static classes mean one global instance that all threads share.... but in the real world it really means that a thread can request something from a static method or class but it is not garunteed to get the actual result expected.  IN other words, a thread requesting info from a static class may pick up the results of another thread that just completed.. instead of what was desired...

 

The issue with having not properly realized  what should and could be marked static - is that it is possible for x user to request updates on orders but y user ends up seeing x's order details instead.  Or, say you have a static class invoked that contains global variables that are used in methods but never 'reset' or re-initialized - you will discover that values expected are actually not be observed across requests.

 Example:

public static class MyWebControl : System.UI.WebControl

{  private variable int i = 0;

   

protected override void OnInit(EventArgs e)

{

  i++

lblNumber.Text = i.ToString();

}

 

Use that code to work up a control and embed it on a page for a simple test.  You will need to use a tool like Mercury or Visual Studio Team Suite, as the effects will appear under load and not necessarily testing through a browser.   What you will see is that because it is static - that the variable will constantly increment and retain the incremented value across any number of web requests - in other words it will never be zero after it has been acessed just once....unless an IISRESET is performed or the worker process is recycled ...If you have twenty clients accessing the page with the control in it just once - the 20th client will see the value of I as 20...and not 1 ...

Singleton Pattern - Thread Safety Approach

In simple terms one of the programming models adopted is called using the Singleton Pattern which means each thread instigates and disposes of any object or code - in other words if you have 20 concurrent requests each request will load into memory the associated dll's etc and no other thread or process can touch it.  This pattern is considered to be the most thread safe approach to programming.

If marking Static results in thread safety issues why use it?

Simple - there are instances where you can use static classes.  Classes that utilize methods that do not use global variables that can change or are programmed to change (like the the integer increment example noted earlier) and instead use parameter declarations that never change - can be used static methods or classes.  In fact static methods will reduce your memory footprint and improve performance.  The reason why, is there is only one instance of a static class in memory in which case there will be nomimal if any GC collections performed against the class and associated methods.  This adventageous in high load scenarios as you will not see the effects on overall TPS and response times when serious GC Gen 0 collections occur...

Keep this in mind as I had a little 'proof is in the pudding' demonstration where I compared a properly constructed static class versus the Singelton Pattern approach.  My GC collections where < 1 (amost 0) and the Singeton Pattern GC's were 2-5 per second....additionally I was able to utilize 2 megs less memory allocation than the same code written to use the Singleton Pattern... Keep in mind this was a simple application - and in a larger scale application scenario this can be incredibly significant.

Is SINGLETON the only way to have THREAD SAFETY?

So, while the Singleton Pattern is a really good programming approach, it relies upon the creating multiple objects in memory which again the Garbage Collection process has to dispose - for each thread or instance created.  It is a no brainer - that while thread safe - it comes with a penality in terms of overall performance and system resources impact.  Depending on your application - well - it can be more or less beneficial overall in terms of the trade-offs.  So, I am not saying that is a bad practice at all - in fact I agree with the programming model for what it is intended to prevent and accomplish - but it does comes with a price and there is an alternative....

Caution....

If you are runing a n-tier application you are most likely using a Model, Utility, Business Layer, and Data Access Layer, which probably results in the incorporation of Interfaces and Factory Classes.

Performance TIP:

If you have say a Factory Class - seal it.  for example:

namespace MyApp.DALFactory

{

public sealed class Customer

{

public static MyApp.IDAL.ICustomer Create()

{

/// Look up the DAL implementation we should be using

string path = Util.WEBDAL;

 

string className = path + ".Customer";

// Using the evidence given in the config file load the appropriate assembly and class

return (MyApp.IDAL.ICustomer)Assembly.Load(path).CreateInstance(className);

}

}

}

Why Seal? 

 

Performance optimizations by the CLR - sealing a class permits certain compiler optimizations that are not possible with non-sealed classes.  Sealed classes however can not be base classes which prevents any overriding of methods by other developers.

 

How to Instigate a Sealed class and oh.... Beware..

To be totally honest with you, the reason for this blog entry is because my client kept stressing thread safety.  My client's programming perspective varied slightly from mine in terms of the approach we both take. The client's approach was the Singelton Pattern, to prevent other developers from modifying the code and injecting non-thread safe code. 

My approach was to code knowing what to expect - not expecting other developers to modify the code or if they did understand the code to properly implement thread safe modifications.... 

 

The mere fact is that even though we solved the same problems differently - using two different approaches - performance wise we were within 1% of each others implementation in terms of agreed upon performance. The difference of the design patterns applied however, were differentiated in how the GC behaved and the overall TPS over time was maintained. 

This is the issue with performance testing. You can have higher cyclidic TPS but if the TPS fluctuates during GC collections or dips, the average naturally is influenced by the lower TPS during the lower end of the cyclidic TPS range.  My programming model demonstrated the same same overall TPS albiet without the same Peak TPS seen via the other model but remains steady regardless of queueing.  In other words - the end TPS in both models was within 1-5% of each other - so - no right or wrong less the behavior you want to have observed in analyzing performance.

 

So the catch...

 

I use sealed factory classes n my n-tier designs.  When I need to instigate say a DAL method - I usually do this:

 

public partial class Quotes : System.Web.UI.Page

{

public static readonly IServiceClient bll = MyApp.BllFactory.DataAccess.Create(MyApp.Utility.Util.ReturnMode);

... other code

Note the 'static' declaration.  When this is invoked from the calling class or method - the entire class that the bll references will inherit the static declaration.  The actual bll clas is not marked static.  Yet, it still  means if you have some global parameter within the assembly the IDAL loads based on the Factory class configuration - it will not be thread safe.  Merely because the bl was invoked usig the static denotation. 

 

 Again - anytime I use the n-tier approach, the only time there are global parameters that have the possibility to change are in the actual page code behind or component and are never marked static.  My own philosophy in programing is that BLL and DAL layers should contain nothing but methods that simply require passed in parameters.  If there are counters or other variables that need to changed I make them local to the method and not global.  Or if I have global  parameters - I set them as new (example: myarray = new array();) before using them in a method...Hence even using static implementations - my code is thread safe...

 

I'll admit that I didn't think my method of programming could ever be unsafe, and in my code and how I approach programming - thread safety has never been an issue. My client did however demonstrate why they didn't feel it was thread safe if other developers started adding in adhoc global parameters that didn't get reset or used in accordance with how I know it should be handled...And I'll  admit I forced them to do it and then declared - but I do not program that way - I know what I should be expecting and code to that...

Again I use the  "public static readonly ISomeFactory" declaration for any BLL or DAL instigation because it is only one instance in memory that the whole application shares and I know that my code is thread safe and I am not worried about someone else tampering with it...But the lesson learned - is that the static denotation renders any code thread unsafe possibly.  In the forementioned example however, to make it thread safe - remove the static denotation - and then each time called a new instance is created as well as memory allocation....

 

Summary: 

 

One of the cool things about this entry is not proving a right way or a wrong way.  I can see both sides of the fence and was glad to be enlightened how my code could be rendered thread unsafe...  However, I was glad that the client literally had to introduce something to my code to show how it could be non thread safe.  Yet I was absolved in my methods of using a static implementation - as it was still thread safe and benefited from less GCs and overall smaller memory footprint.

 

In a few weeks, I'll introduce a supplementary application that you can use to test against and see the results for yourself...

 

I hope this enlightens you to a few poorly or non-searchable documented features of working with static classes;  and, particularly how to work with DAL  / Factory classes where you implement 'static' . Perhaps if you are running into issues where data is not what it supposed to be - the term thread safe - will now be ingrained in your memory - and even more importantly - you know how to resolve it...

 

 

 

 

Posted: Thursday, February 22, 2007 9:48 PM by Jody

Comments

No Comments

New Comments to this post are disabled