2010-12-10 37 views
2

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

我很紧张,因为我是新来的多线程概念,我读过你不应该锁定一个公共对象(我的应用程序变量有效)。我还读到,如果锁定的代码块失败(如果Web服务失败,可能会出现这种情况),那么它可能会破坏应用程序域并导致应用程序崩溃。

我应该提到,我的网站很少使用第三方Web服务,并且很少有2个请求是在同一时间完成的。

下面是我如何使Web服务调用一个粗略的代码示例:

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

回答

1

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

至于如果通话失败导致应用程序不稳定,那必须是由于通话没有正确处理。通过使用“使用”语句,您可以确保在调用结束时调用dispose。请阅读SO thread,了解为什么不应该处理有关性能的Web服务。

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

第三方Web服务具有昂贵的登录方法,所以实际上我不想在每次调用CallThatNeedsToBeSynchronized方法时处理该对象。我想让服务登录并将其存储在应用程序状态中。 – 2010-12-10 20:52:07

+0

然后删除使用块并创建您自己的处理,如果您需要重新创建Web服务。 – 2010-12-10 20:57:30

1

你应该创造,使Web服务调用的类private static readonly object _lock = new object();,并使用它作为一个锁。由于对象是静态的,只会有其中的一个贯穿所有应用程序的,如果你想一个单独的对象(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))。请记住,这意味着,如果您有几种方法,对一个方法的调用也会阻止所有其他方法,这就是您通常不应该锁定这个方法的原因。

2

如果这是至关重要的,那么您应该只需要随时调用一次服务,我建议您编写自己的Windows服务。这取决于您需要多少容错。

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

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

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

编辑

另一种选择是使用直接在监视器对象:

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

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

    } 

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

+0

我明白了你的观点,但在我的情况下,我的并发请求数很少,如果偶然有两次调用由应用程序池回收而产生的Web服务,那么对我来说这不是什么大问题。所以创建一个单独的服务可能会矫枉过正(对我来说)。它确实让我意识到,如果像蜘蛛这样的东西在几秒钟内多次击中我网站上的某个页面,那么我可以锁定很多线程,并调用Web服务。我必须警惕这一点。 – 2010-12-10 20:56:06

0

lock()不会阻止多次调用您的web服务。它只会确保没有线程同时在lock() {}内执行代码块。 所以问题是这个webservice做什么?

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缓存对象或只是一个静态变量。

+0

Web服务是与NetSuite会计系统连接的API。它具有插入,更新和删除数据以及查询数据的方法。不幸的是,所有的电话必须同步(出于某种原因)。如果我调用Web服务的任何方法,那么我需要阻止对该服务的所有调用,直到该方法返回。我认为这个锁会完全阻止对象的访问,但它听起来像是阻止了代码锁块内的调用。 – 2010-12-13 16:21:04

+0

正确。您传递给lock()的对象只是您的锁的“钥匙”。这就是说你可以使用同一个对象来锁定不同的代码块。正如'lock(objWebService){objWebService.foo();}'和'lock(objWebService){objWebService.bar();}'完全可以使foo和bar相互阻塞。 – 2010-12-13 17:51:12