Synchronizing Shared Resources
page 1 of 1
Published: 15 Mar 2003
Unedited - Community Contributed
Abstract
An example illustrating .NET threading to synchronize Web Service access to a legacy single user database.
by Steve Sharrock
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 26621/ 32

Synchronizing Shared Resources

The .NET Framework includes a rich set of  classes within the System.Threading namespace that allow the programmer to control access to shared resources using a myriad of techniques. One of the WaitHandle derived classes, Mutex, provided a perfect solution to a problem I faced synchronizing access to a legacy database from an XML Web Service.

Project Background

My client has a rather large legacy desktop application written in C++ using the MFC. The backend database is a propriety 3rd party package that is apparently implemented for single user access only. My task was to develop an XML Web Service and a web application front end to replace much of the existing desktop application.

The client had already written a .NET Managed C++ interface library to provide access to the unmanaged backend database. This made it very easy to implement the Web Service--I mostly just wrapped the Managed C++ interface exposing the appropriate methods as [WebMethod] in the C# Web Service code.

As we moved from single user unit testing to more extensive testing with multiple users, we discovered very quickly that the unmanaged code would crash frequently. It wasn't much of leap to figure out that the unmanaged code needed to be synchronized.

Solution: Mutex

There are several synchronization locks and techniques available in the Framework, but one in particular seemed perfect for this scenario. A Mutex is a named synchronization object derived from the WaitHandle class that allows for creation of mutually exclusive regions of code. Since a Mutex has a name, it can be used across processes--a requirement in our case because we are crossing several boundaries venturing into the unmanaged legacy code.

I first created a class to encapsulate the System.Threading.Mutex object. During construction, the object obtains a "timeout" parameter from the Web Services' web.conifg file.  If the specified timeout value is missing or in error, it defaults to 15 seconds. If the timeout value is zero, then the Mutex is not implemented and the operation using the DBMutex object will not wait for exclusive access.

private class DBMutex
{
  private int timeout;
  private Mutex mutex;
 
  public DBMutex()
  {
    string val =
    ConfigurationSettings.AppSettings["MutexTimeOut"];
    if ( val == null )
      val = "15";
    try {
      timeout = Convert.ToInt32( val );
    }
    catch {
      timeout = 15;
    }   
    if ( timeout < 1 )
      timeout = 0;
    timeout *= 1000; // milliseconds

    if ( timeout == 0 )
      mutex = null;
    else    
     mutex = new Mutex( false, "DBMutex" );
  }
 
  public bool WaitOne()
  {
    if ( timeout == 0 )
      return true;
    else return mutex.WaitOne( timeout, false );
  }

  public void Release()
  {
    if ( timeout > 0 )
      mutex.ReleaseMutex();
  }
}

Other than contruction, the only other methods implemented mirror the Mutex classes WaitOne and Release methods. The WaitOne method will block the caller if the resource is already locked. If the timeout period is exhausted while waiting, this method will return false; otherwise true is returned to indicate that the resource is now locked exclusively for the caller.

The remaining task is to insure that each Web Service [WebMethod] that tries to access the legacy database first obtains the exlusive DBMutex lock, and then releases the lock as soon as the function call into the managed and unmanaged code returns.

[WebMethod]
public Results GetDBInfo( int infoID )
{
  DBMutex mutex = new DBMutex();
  if ( ! mutex.WaitOne() )
    return new Results( "Exclusive Lock Failure" );

  LegacySystem.DBAccess db = new LegacySystem.DBAccess();
  Results info = db.GetDBInfo( infoID );

  mutex.Release();
  return info;
}

Without getting into implementation details, the Results (return) class encapsulates a DataSet and can indicate an error condition as illustrated when the mutex.WaitOne() fails. The LegacySystem.DBAccess class is part of the interface library written by the client in Managed C++.

Summary

This was a very simple solution to our problem that seems to be working just fine. By adjusting the timeout values for both the Web Service and the Mutex WaitOne() method call, we have balanced the user load that we experience, while still not causing the web application and Web Service to fail if the backend database should become unavailable.

Although we don't need it in our application (as yet), one side effect of the Mutex crossing process boundaries is that the legacy desktop application could use the same named Mutex with the Windows API to provide the same synchronized access employed by the Web Service and managed library interface.

Send your comments and let me know what you think of this article: steve@sharkcode.com

Steve Sharrock -   www.AspAlliance.com/shark / www.SharkCode.com



User Comments

Title: great article   
Name: jeff
Date: 12/6/2006 6:43:06 PM
Comment:
Thanks for that tuturial. It worked perfectly. I was pulling my hair out trying to figure out how to queue my ado requests to my object. I was designing some elabroate remoting singleton archictecture when I came acrross your aritcle. I tweaked your implementation a little bit to suit my needs and presto no more access violations due to concurrency.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2014 ASPAlliance.com  |  Page Processed at 11/27/2014 1:33:41 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search