2012-12-16 80 views
18

我寫了一個控制器和我作爲服務使用的操作。 此服務運行的代價非常高。 如果已經有一個當前正在運行的操作,我想限制對此操作的訪問。如何鎖定一個asp.net mvc操作?

是否有任何內置的方式來鎖定一個asp.net mvc行動?

感謝

+0

你想限制其每用戶,每會話,每個應用程序? – Oshry

+1

您正在尋找隊列。 –

+0

總的限制,不允許任何人進入,如果它已經被稱爲 – vondip

回答

38

你在尋找類似的東西嗎?

public MyController : Controller 
{ 
    private static object Lock = new object(); 

    public ActionResult MyAction() 
    { 
     lock (Lock) 
     { 
      // do your costly action here 
     }  
    } 
} 

上面將防止任何其他線程執行動作如果一個線程是目前lock塊內的處理的代碼。

更新:這裏是如何工作的

方法代碼總是由一個線程執行。在負載很重的服務器上,可能有2個或更多不同的線程並行進入並開始執行方法。根據這個問題,這是你想要防止的。

請注意如何private Lock對象是static。這意味着它在控制器的所有實例中共享。因此,即使在堆上構建了這個控制器的兩個實例,它們都共享相同的Lock對象。 (該對象甚至不必被命名爲Lock,您可以將其命名爲Jerry或Samantha,並且它仍然用於相同的目的。)

以下是發生的情況。您的處理器一次只能允許1個線程輸入一段代碼。在正常情況下,線程A可以開始執行代碼塊,然後線程B可以開始執行它。所以理論上你可以有2個線程同時執行相同的方法(或任何代碼塊)。

lock關鍵字可用於防止這種情況。當一個線程輸入一個包含在lock部分中的代碼塊時,它會「拾取」鎖定對象(在lock關鍵字之後的括號中的內容,例如Lock,JerrySamantha,應該將其標記爲static字段)。在鎖定部分正在執行的時間段內,它「保持」鎖定對象。當線程退出鎖定部分時,它會「放棄」鎖定對象。 從線程拾取鎖定對象的時間開始,直到它放棄鎖定對象,所有其他線程都無法進入代碼的鎖定部分。實際上,它們被「暫停」,直到當前正在執行的線程放棄鎖定對象。

因此,線程A在MyAction方法的開始處拾取鎖對象。在放棄鎖對象之前,線程B也會嘗試執行此方法。但是,它不能拾取鎖對象,因爲它已經被線程A保存。所以它等待線程A放棄鎖對象。當它發生時,線程B就會拿起鎖對象並開始執行代碼塊。當線程B完成該塊的執行後,它將放棄下一個委託處理此方法的線程的鎖定對象。

......但我不知道這是你在找什麼...

使用這種方法並不一定使你的代碼運行得更快。它只能確保一段代碼一次只能由1個線程執行。它通常用於併發原因,而不是性能原因。如果您可以提供有關您在問題中的具體問題的更多信息,可能會有比這更好的答案。

記住我上面給出的代碼將導致其他線程中執行的塊之前等待。如果這不是你想要的,並且你希望整個動作被「跳過」,如果它已經被另一個線程執行了,那麼使用更像Oshry的答案。您可以將此信息存儲在緩存,會話或任何其他數據存儲機制中。

+0

不應該鎖定被保存到會話或緩存對象嗎? – vondip

+0

爲什麼你認爲它需要被保存到會話或緩存? – danludwig

+0

由於http沒有會話,我需要在整個會話中保存鎖定,不是嗎? – vondip

2

您可以創建一個像[UseLock]按您的要求定製的屬性,你的行動

0

之前把它做的是保存到緩存指示作用布爾值是最簡單的方法運行所需的BL:

if (System.Web.HttpContext.Current.Cache["IsProcessRunning"]) 
{ 
    System.Web.HttpContext.Current.Cache["IsProcessRunning"] = true; 
    // run your logic here 
    System.Web.HttpContext.Current.Cache["IsProcessRunning"] = false 
} 

當然,您也可以將其作爲屬性來執行此操作。

+3

你可以有兩個線程if條件裏面走到一起。這不夠好恐懼 – vondip

+0

如何在if條件下獲得兩條線程? 這幾乎是不可能的,但如果您必須絕對確定,則可以將分配鎖定到緩存。 – Oshry

+6

這是一個非常骯髒的競爭條件錯誤的食譜。如果兩個線程執行相同的事情會發生任何問題,請使用鎖定。 – Vincent

5

已經閱讀並接受上述答案我想稍微不同的解決方案達成一致: 如果要檢測到動作的第二個電話,使用Monitor.TryEnter:

if (!Monitor.TryEnter(Lock, new TimeSpan(0))) 
{ 
    throw new ServiceBusyException("Locked!"); 
} 
try 
{ 
... 
} 
finally { 
    Monitor.Exit(Lock); 
} 

使用相同的靜態鎖定對象通過@danludwig

0

詳見我有有關建議。

1- https://github.com/madelson/DistributedLock 系統範圍的鎖溶液

2-遲髮型BackgroundJob.Enqueue與[DisableConcurrentExecution(1000)]屬性。

兩個溶液未決用於待完成的過程。我不想在請求相同時間時拋出錯誤。