如何锁定asp.net mvc动作?

时间:2012-12-16 11:53:41

标签: asp.net-mvc

我已经编写了一个控制器和操作,我将其用作服务。 这项服务运行起来非常昂贵。 如果已经有当前正在运行的操作,我想限制对此操作的访问。

是否有内置方法来锁定asp.net mvc操作?

由于

6 个答案:

答案 0 :(得分:47)

你在找这样的东西吗?

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

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

如果线程当前正在处理lock块中的代码,则上述操作将阻止任何其他线程执行操作。

更新:以下是此方法的工作原理

方法代码总是由线程执行。在负载很重的服务器上,可以有2个或更多不同的线程进入并开始并行执行方法。根据问题,这是你想要防止的。

注意private Lock对象是static的方式。这意味着它在控制器的所有实例之间共享。因此,即使在堆上构造了此控制器的2个实例,它们也共享相同的Lock对象。 (该对象甚至不必命名为Lock,您可以将其命名为Jerry或Samantha,它仍然可以用于相同的目的。)

以下是发生的事情。您的处理器一次只允许1个线程输入一段代码。在正常情况下,线程A可以开始执行代码块,然后线程B可以开始执行它。因此理论上你可以让2个线程同时执行相同的方法(或任何代码块)。

lock关键字可用于防止这种情况发生。当一个线程进入包裹在lock部分中的代码块时,它会“拾取”锁定对象(lock关键字后的括号中的内容,即Lock,{{1 }}或Jerry,应标记为Samantha字段。在执行锁定部分的持续时间内,它“保持”锁定对象。当线程退出锁定部分时,它“放弃”锁定对象。 从线程获取锁定对象开始,直到它放弃锁定对象,阻止所有其他线程进入锁定的代码段。实际上,它们会“暂停”,直到当前正在执行的线程放弃锁定对象。

因此,线程A会在MyAction方法的开头拾取锁定对象。在放弃锁定对象之前,线程B也尝试执行此方法。但是,它无法获取锁定对象,因为它已被线程A保留。因此它等待线程A放弃锁定对象。当它执行时,线程B然后获取锁定对象并开始执行代码块。当线程B完成执行块时,它会放弃委托处理此方法的下一个线程的锁定对象。

...但我不确定这是否是你要找的......

使用此方法不一定会使您的代码运行得更快。它只确保一个代码块一次只能由1个线程执行。它通常用于并发原因,而不是性能原因。如果您可以在问题中提供有关您的特定问题的更多信息,可能会有比这更好的答案。

请记住,上面介绍的代码会导致其他线程在执行块之前等待。如果这不是您想要的,并且您希望整个操作被“跳过”(如果它已被另一个线程执行),那么请使用更像Oshry的答案。您可以将此信息存储在缓存,会话或任何其他数据存储机制中。

答案 1 :(得分:6)

阅读并同意上述答案后,我想要一个稍微不同的解决方案: 如果要检测对操作的第二次调用,请使用Monitor.TryEnter:

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

使用与@danludwig

详述的相同的静态Lock对象

答案 2 :(得分:2)

您可以根据自己的要求创建[UseLock]等自定义属性,并将其放在行动

之前

答案 3 :(得分:1)

我对此有所建议。

1- https://github.com/madelson/DistributedLock 系统范围的锁定解决方案

2- Hangfire BackgroundJob.Enqueue with [DisableConcurrentExecution(1000)]属性。

两个解决方案正在等待处理完成。我不想在同时请求时抛出错误。

答案 4 :(得分: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
}

当然,您也可以将此类或类似内容作为属性执行。

答案 5 :(得分:0)

我更喜欢使用SemaphoreSlim,因为它支持异步操作。

如果需要控制读/写,则可以使用ReaderWriterLockSlim

以下代码片段使用SemaphoreSlim

public class DemoController : Controller
{
    private static readonly SemaphoreSlim ProtectedActionSemaphore =
        new SemaphoreSlim(1);

    [HttpGet("paction")] //--or post, put, delete...
    public IActionResult ProtectedAction()
    {
        ProtectedActionSemaphore.Wait();
        try
        {
            //--call your protected action here
        }
        finally
        {
            ProtectedActionSemaphore.Release();
        }

        return Ok(); //--or any other response
    }

    [HttpGet("paction2")] //--or post, put, delete...
    public async Task<IActionResult> ProtectedActionAsync()
    {
        await ProtectedActionSemaphore.WaitAsync();
        try
        {
            //--call your protected action here
        }
        finally
        {
            ProtectedActionSemaphore.Release();
        }

        return Ok(); //--or any other response
    }
}

希望对您有帮助。