Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Pooled Objects and Initialization Control in VS.NET : Page 2

One of .Net's more obvious benefits for today's Visual Basic developers, is the ability to easily create poolable objects using Visual Basic.NET. The author demonstrates how a web application that uses poolable objects, can show a 50% performance boost in the rate of transactions per second when pooling is enabled. The article also covers things such as application configuration, string resources, page templates, and using XSL stylesheets.


advertisement

Simple Code Shell
Now that you’ve got a mapping for an HTTPHandler class, let’s take a closer look at a simple code shell for such a class.

Public Class WAC : Implements System.Web.IHttpHandler

   Shared s_InitGUID As String
   Private m_InitGUID As String



   Public Sub ProcessRequest(ByVal Context As HttpContext) _
          Implements IHttpHandler.ProcessRequest

      ' Initialize...
      zInit()
      ' Process the transaction...
   End Sub

   Public ReadOnly Property IsReusable() As Boolean _
          Implements IHttpHandler.IsReusable
      Get
         Return True
      End Get
   End Property

   Public Shared Sub Refresh() 
      ' This sets the shared InitGUID to a different 
      ' value than that of the module level InitGUID.
      ' This will trigger an initialization on the 
      ' next object access, at which time a new shared
      ' InitGUID will be assigned. Following this, all
      ' loaded instances will one by one reinitialize
      ' themselves on their next access.

       s_InitGUID = ""
   End Sub

   Private Sub zInit()
      ' This method takes care of the initialization
      ' of objects which are shared across transactions.
      ' Since this is a pooled object, sharing generalized
      ' overhead operations results in an increase in 
      ' performance. However, care must be taken to ensure
      ' that transaction specific data from a previous
      ' transaction does not impact on subsequent transactions.

      If s_InitGUID = "" Then
         s_InitGUID = GUID.NewGUID.ToString
      End If

      If s_InitGUID = m_InitGUID Then
         Exit Sub
      End If

 ' Do your initialization here.

      ' Then synchronize the private and shared markers.
      m_InitGUID = s_InitGUID
   End Sub

End Class



It’s really quite simple. When IIS instantiates this class, or pulls it from the pool, it calls ProcessRequest, passing Context in as a parameter. From that point on you’ve got access to everything you need in order to process the request and send the response back to the browser.

The only other method which this interface provides is the IsReusable method which returns a Boolean. Simply return True and when control returns to IIS, it will release the object into a pool, rather than destroy it. The main thing to understand about pooling, is that the object will maintain its state between calls. This is a double edged sword. State which is generic, and relevant to all transactions across the board, should be maintained in order to eliminate the need to re-create it on successive transactions. State which is accumulated at the behest of a specific transaction must be either explicitly initialized at the beginning of ProcessRequest, or de-initialized at the end of ProcessRequest. Failing to do this might result in state from a previous transaction, having an adverse effect on a subsequent transaction.

In my application, the generic state which I maintain between transaction consists of several XML DOM’s created from external XML files. Consequently, the ability to cache this loaded information in a pooled object yields a significant performance improvement.

There is one drawback to this scheme however, since it is possible for the cached information in a pooled object to get out of sync with the information in the external files. To be sure, this is the exception rather than the rule since, by and large, the contents of these files don’t normally change on a day-to-day basis. However, it would be very convenient to have a mechanism whereby these pooled objects could be synchronized with the current external data without having to restart the web server.

I briefly considered using file modification dates in order to accomplish this, but quickly rejected this since I didn’t want to have to expend effort on each transaction for an occurrence which might happen only once a month. The method I chose leverages the new ability to define shared data variables in Visual Basic.NET.

The concept behind a shared variable is very similar to a variable in a VB6 global BAS module. That is, that all instances of the class loaded on the same thread will share that variable. Similarly, with Visual Basic.NET, all instances of a class in the same AppDomain (roughly equivalent to a process) will share the same Shared variable. So this capability provides us with the basic approach. Using a shared variable as a toggle switch, it is possible to reach out and touch’ all class instances in order to inform them that some event has or should occur.

However, a single shared data point is insufficient for the operation we are contemplating. If the shared data point was the only value involved, then the first class instance to re-initialize itself would reset the toggle to indicate its own re-initialization has occurred, thus short-circuiting the mechanism for other class instances. The solution is to utilize two data points, a shared variable which is shared across all class instances, and a private variable which is specific to each class instance. In the example above, these two data points are s_InitGUID and m_InitGUID respectively.

The way the scheme works is that, basically, anytime a class initializes it copies the shared initialization GUID to it’s own private initialization GUID. As long as those two items are in sync, then the class does not re-initialize itself. This check is a simple, low-cost, string comparison which takes place at the beginning of zInit, which is the initialization routine called upon each entry into ProcessRequest.. Although zInit is invoked on every request, it rarely completes, since usually it finds the two variables are equivalent and exits immediately.

If you’re after the ultimate tweak, and don’t mind cluttering up your main line code a bit, then you can use the following branch to avoid going into zInit in the first place, unless you actually need to.

 

      If m_InitGUID = "" Or m_InitGUID <> s_InitGUID Then

         zInit()

      End If

 

In this case you won’t need the following conditional logic inside zInit.

 

      If s_InitGUID = m_InitGUID Then

         Exit Sub

      End If

Here’s how the scheme works, starting from application startup:
  • The first class to be instantiated finds s_InitGUID = ''. It assigns a baseline GUID to s_InitGUID which is then copied to its own m_InitGUID.

  • Subsequent classes which are instantiated find their internal s_InitGUID = '' which is different, after all, than the shared GUID. They therefore initialize themselves and assigned the shared initialization GUID to their own internal initialization GUID.

  • As classes are retrieved from the pool, they find that their internal GUID matches the shared GUID. They bypass initialization.

  • If the shared Refresh method is called, it will reset the shared GUID string to ''.

  • The next class to be instantiated or retrieved from the pool, finds s_InitGUID = ''. It assigns a new baseline GUID to s_InitGUID which is then copied to its own m_InitGUID.

  • Subsequent classes which are either instantiated, or retrieved form the pool, find their internal GUID’s to be different, than the shared GUID. They therefore initialize themselves and assigned the shared initialization GUID to their own internal initialization GUID.

  • Subsequent classes which are retrieved from the pool, and have already been re-initialized, find their internal GUID’s to be equal to the shared GUID. They therefore bypass initialization.

The following table summarizes this activity.

Condition

Implication

s_InitGUID = ""


Class needs to initialize. This class is the first class to be instantiated since the site was started, or the first instance to be retrieved from the pool since the shared Refresh method was called.

s_InitGUID <> m_InitGUID
Class needs to initialize since the GUID which marks the most recent initialization is out of date with the current shared initialization GUID.
s_InitGUID = m_InitGUID
The initialized data in this class instance is up to date.

I'd like to emphasize that, in many ways, the methodology of what I have presented is more significant than the actual details are. I’d like to focus in on two specific points.

The first point is that multiple instances of a pooled class can maintain their own copies of application wide data. Some readers may object. So what? This functionality is already available in several different manners. Even now, without pooled objects we keep our application wide data in centralized repository such as either the Application object, the Shared Property Manager (SPM) or the Global Information Table (GIT)’. These readers might also note that by using Shared class variables rather than Private class variables to maintain the data, only a single copy of the data needs to be maintained.

It is true that these are all viable alternatives. However the use of pooled objects and class private variables address certain circumstances for which the alternatives listed above are not viable. I’ll present a few examples in order to demonstrate that this method is simply one more tool in our development arsenal, to be used appropriately (as all tools should be used).
  • Storing a VB6 object, or any thread-affinitive object, for global access, has always been problematic, since this, in effect, serializes every transaction which accesses the global object through a single thread. The technique presented above addresses this issue handily since each pooled transaction object maintains it’s own instance of the global access class(es) in its own class private variable(s).
  • Multiple clients accessing a common data repository involves problems of synchronization if one client tries to access the data while another client is updating the data. The technique presented above eliminates this issue entirely since each client maintains its own private copy of the data.
  • The use of class private data is also viable for data which is likely to be application wide, but for which exceptions are bound to occur for specific transactions. A good example of this is a resource file. If we assume that 90% of the users are native language speakers, while 10% are foreigners, then we can use an optimistic’ algorithm to retain a resource file in preparation for the next transaction. If the next transaction uses the same language (which should occur about 80% of the time) then the language file need not be loaded. If the language of the subsequent transaction differs, then the new language file is loaded. Here we have a situation where we’d like to retain cached data across transactions, yet it’s obvious that this cached data can not be shared among all instances since different simultaneous transactions might be using different language files.




Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date