2

I'm using a 3rd party web service in my ASP.NET application. Calls to the 3rd party web service have to be synchronized, but ASP.NET is obviously multi-threaded and multiple page requests could be made that result in simultaneous calls to the 3rd party web service. Calls to the web service are encapsulated in a custom object. My thought is to store the object in an application variable and use the C# lock keyword to force synchronized use of it.

I'm nervous, because I'm new to multi threaded concepts and I've read that you shouldn't lock a public object (which my application variable effectively is). I've also read that if the locked block of code fails (which it could if the web service fails), then it could destabilize the app domain and bring down the application.

I should mention that the 3rd party web service is rarely used in my website and it's going to be rare that 2 requests to it are made at the same time.

Here's a rough code sample of how I'd make calls to the web service:

ThirdPartWebService objWebService = Application["ThirdPartWebService"] As ThirdPartWebService;
lock (objWebService)
{
  objWebService.CallThatNeedsToBeSynchronized();
}
Ben Mills
  • 27,454
  • 14
  • 42
  • 38

4 Answers4

2

You should create a private static readonly object _lock = new object(); in the class that makes the webservice calls, and use that as a lock. Since the object is static there will only be one of them throughout all of your application, a Singleton object if you wish (http://en.wikipedia.org/wiki/Singleton_pattern)

public class MyWebServiceWrapper
{
   private static readonly object _lock = new object();

   public void CallWebService()
   {
      lock(_lock)
      {
         var objWebService = (ThirdPartWebService)Application["ThirdPartWebService"];
         objWebService.CallThatNeedsToBeSynchronized();
      }
   }
}

If your class that makes the WebService call doesn't do anything else, you can also just make a lock on this (lock(this)). Just remember, that this will mean, that if you have several methods, the call to one method will block all the other methods as well, which is why you normally shouldn't lock this.

Pauli Østerø
  • 6,878
  • 2
  • 31
  • 48
2

If it is vital you should only have a single call to the service at any time I recommend you write your own Windows Service. This depends on how much fault tolerance you want.

Let's say for example you make a call to the web service, but then the application pool is recycled. When a new request comes in it would be handled by a new instance of your application which could then make the call to the web service (Even if the other instance is running).

You could pass this off to a windows a service, then use a polling mechanism from the client to check if the service has finished (Client would ask IIS are you done, IIS would look for some indication from windows service that it was done). This approach will avoid locking anything in IIS, and you won't waste critical resources such as threads in your thread pool waiting on a third party service.

You should never lock on a single resource in your web application...it's just too risky.

Edit

Another option is to use the Monitor object directly:

    if (System.Threading.Monitor.TryEnter(syncObj,10))
    {

        try
        {
            //CallWebService
        }
        finally
        {
            System.Threading.Monitor.Exit(syncObj);
        }
    }
    else
    {
        //Tell Client they are still waiting

    }

TryEnter will block until a lock is made or 10 milliseconds has passed. You could then in your timeout tell the client they need to retry. You could then have your client code decide if it should reissue the request. You could also use a semaphore or mutex (forget which one is more appropiate here). But it would assuming you have permissions to use them, give you something you can lock on at the machine level which would prevent the app recycling use case.

JoshBerke
  • 66,142
  • 25
  • 126
  • 164
  • I see your point, but in my case I'll have very few simultaneous requests and if I happened to have 2 calls to the web service resulting from a recycling of the application pool, then it's not a huge deal for me. So creating a separate service would probably be overkill (again for me). It does make me realize that if something like a spider hit a certain page on my site many times within a few seconds, then I could have a lot of threads locked and making calls to the web service. I'll have to guard against that. – Ben Mills Dec 10 '10 at 20:56
1

You can lock on a static shared object. This is a common way to use lock´s in .Net. By using a static object you know it will be shared among all threads, and the lock is ensured.

As for making the app unstable if the call fails, that has to be due to the call not disposing properly. By using the "using" statement you are ensuring that dispose is called at the end of the call. Read this SO thread on why/why not you should dispose a web service regarding performance.

static readonly object _lockObj = new object();
...
lock( _lockObj )
{ 
   ThirdPartWebService objWebService = Application["ThirdPartWebService"] As ThirdPartWebService;
   objWebService.CallThatNeedsToBeSynchronized();
}
Community
  • 1
  • 1
Mikael Svenson
  • 39,181
  • 7
  • 73
  • 79
  • The third party web service has an expensive login method, so I actually don't want to dispose of the object each time I call the CallThatNeedsToBeSynchronized method. I want to leave the service logged in and stored it in application state. – Ben Mills Dec 10 '10 at 20:52
  • Then remove the using block and create your own handling if you need to recreate the web service. – Mikael Svenson Dec 10 '10 at 20:57
0

lock() will not prevent multiple call to your webservice. It will only ensure that no thread is executing code block within lock() {} at the same time. So the the question is what does that webservice do?

1) Performs some action on third party (updates their DB with some values you supply?) You can do as you've yourself suggested. Though I would say that if their service cannot handle simultaneous calls, then they should fix it. Thats really not your problem to worry about.

2) It queries and returns some data for your use. In this case lock is useless unless you plan on caching the result of the call.

var cachedValue = ReadValueFromCache();
if (cachedValue != null)
    return cachedValue;

lock (objWebService)
{
    // yes you need to do it second time inside the lock
    cachedValue = ReadValueFromCache();
    if (cachedValue != null)
        return cachedValue;

    cachedValue = objWebService.CallThatNeedsToBeSynchronized();
    SaveValueToCache(cachedValue);
}

return cachedValue;

How you implement the cache is kinda secondary. It maybe web cache object or just a static variable.

Ilia G
  • 10,043
  • 2
  • 40
  • 59
  • The web service is an API to interface with the NetSuite accounting system. It has methods to insert, update and delete data, as well as for querying data. Unfortunately, all the calls have to be synchronized (for some reason). If I call any method of the web service, then I need to block all calls to the service until that method has returned. I thought that lock would completely block access to the object, but it sounds like it just blocks the calls within the lock block of code. – Ben Mills Dec 13 '10 at 16:21
  • correct. The object that you pass into lock() is just a "key" for your lock. That said you can use the same object to lock different blocks of code. As in having `lock(objWebService) {objWebService.foo();}` and `lock(objWebService) {objWebService.bar();}` will totally work in making foo and bar block each other. – Ilia G Dec 13 '10 at 17:51