2010-12-10 80 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