如何锁定asp.net mvc动作?

von*_*dip 24 asp.net-mvc

我编写了一个控制器和动作,我将其用作服务.这项服务运行成本很高.如果已经有当前正在运行的操作,我想限制对此操作的访问.

有没有内置的方法来锁定asp.net mvc动作?

谢谢

dan*_*wig 50

你在找这样的东西吗?

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

    public ActionResult MyAction()
    {
        lock (Lock)
        {
            // do your costly action here
        }    
    }
}
Run Code Online (Sandbox Code Playgroud)

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

更新:这是如何工作的

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

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

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

lock关键字可用于防止这种情况发生.当一个线程进入的包裹在一个代码块lock部分,它"拿起"锁对象(什么是括号中的后lock关键字,又名Lock,Jerry,或Samantha,其中应该被标记为一个static场).在执行锁定部分的持续时间内,它"保持"锁定对象.当线程退出锁定部分时,它"放弃"锁定对象.从线程获取锁定对象开始,直到它放弃锁定对象,所有其他线程都被阻止进入锁定的代码段.实际上,它们被"暂停",直到当前正在执行的线程放弃锁定对象.

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

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

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

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


小智 6

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

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

使用与@danludwig详细说明的相同的静态Lock对象


dbv*_*ega 6

我更喜欢使用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
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望它有帮助。