2012-12-16 78 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)]属性。

两个溶液未决用于待完成的过程。我不想在请求相同时间时抛出错误。