锁定ASP.NET应用程序变量

时间:2010-12-10 20:27:41

标签: c# asp.net multithreading locking

我在ASP.NET应用程序中使用第三方Web服务。必须同步对第三方Web服务的调用,但ASP.NET显然是多线程的,并且可以进行多个页面请求,从而同时调用第三方Web服务。对Web服务的调用封装在自定义对象中。我的想法是将对象存储在应用程序变量中,并使用C#lock关键字强制同步使用它。

我很紧张,因为我是多线程概念的新手,我读过你不应该锁定一个公共对象(我的应用程序变量是有效的)。我还读过,如果锁定的代码块失败(如果Web服务失败可能会失败),那么它可能会破坏应用程序域的稳定性并降低应用程序。

我应该提到第三方网络服务很少在我的网站上使用,并且很少会同时发出2个请求。

以下是我如何调用Web服务的粗略代码示例:

ThirdPartWebService objWebService = Application["ThirdPartWebService"] As ThirdPartWebService;
lock (objWebService)
{
  objWebService.CallThatNeedsToBeSynchronized();
}

4 个答案:

答案 0 :(得分:2)

如果它至关重要,您应该随时只需拨打一次电话,我建议您编写自己的Windows服务。这取决于您想要的容错程度。

例如,假设您调用Web服务,然后回收应用程序池。当有新请求进入时,它将由您的应用程序的新实例处理,然后该实例可以调用Web服务(即使另一个实例正在运行)。

您可以将其传递给Windows服务,然后使用客户端的轮询机制来检查服务是否已完成(客户端会问IIS你做完了,IIS会从Windows服务中寻找一些指示它是完成)。这种方法可以避免在IIS中锁定任何内容,并且不会浪费线程池中的线程等关键资源等待第三方服务。

您永远不应该锁定Web应用程序中的单个资源......这样做太冒险了。

修改

另一种选择是直接使用Monitor对象:

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

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

    }

TryEnter将阻塞,直到锁定或已经过了10毫秒。然后,您可以在超时中告诉客户端他们需要重试。然后,您可以让您的客户端代码决定是否应该重新发出请求。你也可以使用信号量或互斥量(忘记哪一个更适合这里)。但它会假设你有权使用它们,给你一些你可以在机器级锁定的东西,这会阻止应用程序回收用例。

答案 1 :(得分:1)

您可以锁定静态共享对象。这是在.Net中使用lock的常用方法。通过使用静态对象,您知道它将在所有线程之间共享,并确保锁定。

如果呼叫失败,使应用程序不稳定,这必须是因为呼叫未正确处理。通过使用“using”语句,您可以确保在调用结束时调用dispose。请阅读此SO thread,了解为什么/为什么不应该处理有关性能的Web服务。

static readonly object _lockObj = new object();
...
lock( _lockObj )
{ 
   ThirdPartWebService objWebService = Application["ThirdPartWebService"] As ThirdPartWebService;
   objWebService.CallThatNeedsToBeSynchronized();
}

答案 2 :(得分:1)

您应该在进行Web服务调用的类中创建一个private static readonly object _lock = new object();,并将其用作锁。由于对象是静态的,所以在整个应用程序中只有一个,如果你愿意,可以使用Singleton对象(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();
      }
   }
}

如果您进行WebService调用的类没有执行任何其他操作,您也可以对此进行锁定(lock(this))。请记住,这意味着,如果你有几个方法,对一个方法的调用也将阻止所有其他方法,这就是你通常不应该锁定它的原因。

答案 3 :(得分:0)

lock()不会阻止对您的网络服务的多次调用。它只会确保没有线程同时在lock() {}内执行代码块。 那么问题是该Web服务的作用是什么?

1)对第三方执行某些操作(使用您提供的某些值更新其数据库?) 你可以按照自己的建议去做。虽然我会说,如果他们的服务不能同时处理呼叫,那么他们应该修复它。这真的不是你担心的问题。

2)它查询并返回一些数据供您使用。 在这种情况下,除非您计划缓存调用结果,否则锁是无用的。

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;

如何实现缓存有点次要。它可能是Web缓存对象,也可能只是一个静态变量。