Подтвердить что ты не робот

Как заблокировать действие asp.net mvc?

Я написал контроллер и действие, которое я использую в качестве службы. Эта услуга выполняет довольно дорогостоящие действия. Я хотел бы ограничить доступ к этому действию, если есть уже действующее действие.

Есть ли встроенный способ блокировки действия asp.net mvc?

Спасибо

4b9b3361

Ответ 1

Вы ищете что-то вроде этого?

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

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

Вышеупомянутое предотвратит выполнение каких-либо других потоков, если поток в настоящее время обрабатывает код в блоке lock.

Обновление: вот как это работает

Код метода всегда выполняется потоком. На сильно загруженном сервере можно ввести два или несколько разных потоков и начать параллельный запуск метода. В соответствии с вопросом, это то, что вы хотите предотвратить.

Обратите внимание, что объект private Lock static. Это означает, что он доступен для всех экземпляров вашего контроллера. Таким образом, даже если в куче есть два экземпляра этого контроллера, оба из них имеют один и тот же объект Lock. (Объекту даже не нужно называть Lock, вы могли бы назвать его Джерри или Самантой, и это все равно будет служить той же цели.)

Вот что происходит. Ваш процессор может разрешить только одному потоку вводить секцию кода за раз. В нормальных условиях поток A может начать выполнять блок кода, а затем поток B может начать его выполнение. Поэтому теоретически вы можете иметь 2 потока, выполняющих один и тот же метод (или любой блок кода) одновременно.

Ключевое слово lock можно использовать для предотвращения этого. Когда поток входит в блок кода, завернутый в раздел lock, он "захватывает" объект блокировки (что находится в скобках после ключевого слова lock, aka lock, Jerry или Samantha), который должен быть помечен как поле static). В течение времени, когда выполняется блокировка секции, она "удерживает" объект блокировки. Когда поток выходит из заблокированной секции, он "отказывается" от объекта блокировки. С момента, когда поток захватывает объект блокировки, пока он не отбросит объект блокировки, все другие потоки не смогут войти в заблокированную секцию кода. По сути, они "приостановлены", пока текущий исполняемый поток не отбросит объект блокировки.

Итак, поток A захватывает объект блокировки в начале вашего метода MyAction. Прежде чем он откажется от объекта блокировки, поток B также пытается выполнить этот метод. Однако он не может забрать объект блокировки, потому что он уже удерживается потоком A. Таким образом, он ожидает, что поток A откажется от объекта блокировки. Когда это произойдет, поток B затем берет объект блокировки и начинает выполнение блока кода. Когда поток B завершает выполнение блока, он отказывается от объекта блокировки для следующего потока, который делегирован для обработки этого метода.

... но я не уверен, что это то, что вы ищете...

Использование этого подхода не обязательно сделает ваш код более быстрым. Это гарантирует, что блок кода может выполняться только одним потоком за раз. Он обычно используется по причинам concurrency, а не по соображениям производительности. Если вы можете предоставить больше информации о своей конкретной проблеме в вопросе, может быть лучший ответ, чем этот.

Помните, что код, представленный выше, заставит другие потоки ждать до выполнения блока. Если это не то, что вы хотите, и вы хотите, чтобы все действие было "пропущено", если оно уже выполняется другим потоком, используйте более точный ответ Oshry. Вы можете сохранить эту информацию в кеше, сеансе или любом другом механизме хранения данных.

Ответ 2

Прочитав и согласившись с вышеуказанным ответом, я хотел немного другое решение: Если вы хотите обнаружить второй вызов действия, используйте Monitor.TryEnter:

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

Использовать тот же объект статической блокировки, как описано @danludwig

Ответ 3

Вы можете создать собственный атрибут типа [UseLock] в соответствии с вашими требованиями и поместить его перед своим действием

Ответ 4

У меня есть предложения об этом.

1- https://github.com/madelson/DistributedLock решение для системного замка

2- Флажок "Захват фона фона" с атрибутом [DisableConcurrentExecution (1000)].

Два процесса ожидаются для завершения процесса. я не хочу бросать ошибку при запросе в то же время.

Ответ 5

Я предпочитаю использовать 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
    }
}

Надеюсь, это поможет.

Ответ 6

Самый простой способ сделать это - сохранить в кэше логическое значение, указывающее, что действие уже выполняет требуемый 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
}

Конечно, вы можете сделать это или нечто подобное, как и атрибут.